Merge branch 'develop' of https://github.com/frappe/erpnext into party_account_currency_check
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 566323a..88049be 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -16,3 +16,10 @@
# Whitespace fix throughout codebase
4551d7d6029b6f587f6c99d4f8df5519241c6a86
+b147b85e6ac19a9220cd1e2958a6ebd99373283a
+
+# sort and cleanup imports
+915b34391c2066dfc83e60a5813c5a877cebe7ac
+
+# removing six compatibility layer
+8fe5feb6a4372bf5f2dfaf65fca41bbcc25c8ce7
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 511b682..1cf9a5b 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -23,7 +23,7 @@
1. **Steps to Reproduce:** The bug report must have a list of steps needed to reproduce a bug. If we cannot reproduce it, then we cannot solve it.
1. **Version Number:** Please add the version number in your report. Often a bug is fixed in the latest version
1. **Clear Title:** Add a clear subject to your bug report like "Unable to submit Purchase Order without Basic Rate" instead of just "Cannot Submit"
-1. **Screenshots:** Screenshots are a great way of communicating the issues. Try adding annotations or using LiceCAP to take a screencast in `gif`.
+1. **Screenshots:** Screenshots are a great way of communicating issues. Try adding annotations or using LiceCAP to take a screencast in `gif`.
### Feature Request Guidelines
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index c145291..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,47 +0,0 @@
----
-name: Bug report
-about: Report a bug encountered while using ERPNext
-labels: bug
----
-
-<!--
-Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
-
-1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- - For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
- - For documentation issues, refer to https://github.com/frappe/erpnext_com
-2. Use the search function before creating a new issue. Duplicates will be closed and directed to
- the original discussion.
-3. When making a bug report, make sure you provide all required information. The easier it is for
- maintainers to reproduce, the faster it'll be fixed.
-4. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
--->
-
-## Description of the issue
-
-## Context information (for bug reports)
-
-**Output of `bench version`**
-```
-(paste here)
-```
-
-## Steps to reproduce the issue
-
-1.
-2.
-3.
-
-### Observed result
-
-### Expected result
-
-### Stacktrace / full error message
-
-```
-(paste here)
-```
-
-## Additional information
-
-OS version / distribution, `ERPNext` install method, etc.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
new file mode 100644
index 0000000..a6e16a0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -0,0 +1,106 @@
+name: Bug Report
+description: Report a bug encountered while using ERPNext
+labels: ["bug"]
+
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
+
+ 1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
+ - For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.erpnext.com)
+ - For documentation issues, propose edit on [documentation site](https://docs.erpnext.com/) directly.
+ 2. When making a bug report, make sure you provide all required information. The easier it is for
+ maintainers to reproduce, the faster it'll be fixed.
+ 3. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
+
+ - type: textarea
+ id: bug-info
+ attributes:
+ label: Information about bug
+ description: Also tell us, what did you expect to happen?
+ placeholder: Please provide as much information as possible.
+ validations:
+ required: true
+
+ - type: dropdown
+ id: version
+ attributes:
+ label: Version
+ description: Affected versions.
+ multiple: true
+ options:
+ - v12
+ - v13
+ - v14
+ - develop
+ validations:
+ required: true
+
+ - type: dropdown
+ id: module
+ attributes:
+ label: Module
+ description: Select affected module of ERPNext.
+ multiple: true
+ options:
+ - accounts
+ - stock
+ - buying
+ - selling
+ - ecommerce
+ - manufacturing
+ - HR
+ - projects
+ - support
+ - assets
+ - integrations
+ - quality
+ - regional
+ - portal
+ - agriculture
+ - education
+ - non-profit
+ validations:
+ required: true
+
+ - type: textarea
+ id: exact-version
+ attributes:
+ label: Version
+ description: Share exact version number of Frappe and ERPNext you are using.
+ placeholder: |
+ Frappe version -
+ ERPNext Verion -
+ validations:
+ required: true
+
+ - type: dropdown
+ id: install-method
+ attributes:
+ label: Installation method
+ options:
+ - docker
+ - easy-install
+ - manual install
+ - FrappeCloud
+ validations:
+ required: true
+
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output / Stack trace / Full Error Message.
+ description: Please copy and paste any relevant log output. This will be automatically formatted.
+ render: shell
+
+
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Code of Conduct
+ description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md)
+ options:
+ - label: I agree to follow this project's Code of Conduct
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 6cdad35..418bf3c 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,7 +1,10 @@
---
name: Feature request
about: Suggest an idea to improve ERPNext
+title: ''
labels: feature-request
+assignees: ''
+
---
<!--
diff --git a/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md b/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md
deleted file mode 100644
index 2016bcc..0000000
--- a/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-name: Question about using ERPNext
-about: This is not the appropriate channel
-labels: invalid
----
-
-Please post on our forums:
-
-for questions about using `ERPNext`: https://discuss.erpnext.com
-
-for questions about using the `Frappe Framework`: ~~https://discuss.frappe.io~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/frappe) tagged under `frappe`
-
-for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/frappe/bench)
-
-For documentation issues, use the [ERPNext Documentation](https://erpnext.com/docs/) or [Frappe Framework Documentation](https://frappe.io/docs/user/en) or the [developer cheetsheet](https://github.com/frappe/frappe/wiki/Developer-Cheatsheet)
-
-> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.**
\ No newline at end of file
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 91983d3..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__":
diff --git a/.github/helper/install.sh b/.github/helper/install.sh
index 455ab86..85f146d 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
@@ -42,6 +37,9 @@
sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
bench get-app erpnext "${GITHUB_WORKSPACE}"
+
+if [ "$TYPE" == "server" ]; then bench setup requirements --dev; fi
+
bench start &> bench_run_logs.txt &
bench --site test_site reinstall --yes
bench build --app frappe
diff --git a/.github/helper/semgrep_rules/README.md b/.github/helper/semgrep_rules/README.md
deleted file mode 100644
index 670d8d2..0000000
--- a/.github/helper/semgrep_rules/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Semgrep linting
-
-## What is semgrep?
-Semgrep or "semantic grep" is language agnostic static analysis tool. In simple terms semgrep is syntax-aware `grep`, so unlike regex it doesn't get confused by different ways of writing same thing or whitespaces or code split in multiple lines etc.
-
-Example:
-
-To check if a translate function is using f-string or not the regex would be `r"_\(\s*f[\"']"` while equivalent rule in semgrep would be `_(f"...")`. As semgrep knows grammer of language it takes care of unnecessary whitespace, type of quotation marks etc.
-
-You can read more such examples in `.github/helper/semgrep_rules` directory.
-
-# Why/when to use this?
-We want to maintain quality of contributions, at the same time remembering all the good practices can be pain to deal with while evaluating contributions. Using semgrep if you can translate "best practice" into a rule then it can automate the task for us.
-
-## Running locally
-
-Install semgrep using homebrew `brew install semgrep` or pip `pip install semgrep`.
-
-To run locally use following command:
-
-`semgrep --config=.github/helper/semgrep_rules [file/folder names]`
-
-## Testing
-semgrep allows testing the tests. Refer to this page: https://semgrep.dev/docs/writing-rules/testing-rules/
-
-When writing new rules you should write few positive and few negative cases as shown in the guide and current tests.
-
-To run current tests: `semgrep --test --test-ignore-todo .github/helper/semgrep_rules`
-
-
-## Reference
-
-If you are new to Semgrep read following pages to get started on writing/modifying rules:
-
-- https://semgrep.dev/docs/getting-started/
-- https://semgrep.dev/docs/writing-rules/rule-syntax
-- https://semgrep.dev/docs/writing-rules/pattern-examples/
-- https://semgrep.dev/docs/writing-rules/rule-ideas/#common-use-cases
diff --git a/.github/helper/semgrep_rules/frappe_correctness.py b/.github/helper/semgrep_rules/frappe_correctness.py
deleted file mode 100644
index 745e646..0000000
--- a/.github/helper/semgrep_rules/frappe_correctness.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import frappe
-from frappe import _, flt
-
-from frappe.model.document import Document
-
-
-# ruleid: frappe-modifying-but-not-comitting
-def on_submit(self):
- if self.value_of_goods == 0:
- frappe.throw(_('Value of goods cannot be 0'))
- self.status = 'Submitted'
-
-
-# ok: frappe-modifying-but-not-comitting
-def on_submit(self):
- if self.value_of_goods == 0:
- frappe.throw(_('Value of goods cannot be 0'))
- self.status = 'Submitted'
- self.db_set('status', 'Submitted')
-
-# ok: frappe-modifying-but-not-comitting
-def on_submit(self):
- if self.value_of_goods == 0:
- frappe.throw(_('Value of goods cannot be 0'))
- x = "y"
- self.status = x
- self.db_set('status', x)
-
-
-# ok: frappe-modifying-but-not-comitting
-def on_submit(self):
- x = "y"
- self.status = x
- self.save()
-
-# ruleid: frappe-modifying-but-not-comitting-other-method
-class DoctypeClass(Document):
- def on_submit(self):
- self.good_method()
- self.tainted_method()
-
- def tainted_method(self):
- self.status = "uptate"
-
-
-# ok: frappe-modifying-but-not-comitting-other-method
-class DoctypeClass(Document):
- def on_submit(self):
- self.good_method()
- self.tainted_method()
-
- def tainted_method(self):
- self.status = "update"
- self.db_set("status", "update")
-
-# ok: frappe-modifying-but-not-comitting-other-method
-class DoctypeClass(Document):
- def on_submit(self):
- self.good_method()
- self.tainted_method()
- self.save()
-
- def tainted_method(self):
- self.status = "uptate"
diff --git a/.github/helper/semgrep_rules/frappe_correctness.yml b/.github/helper/semgrep_rules/frappe_correctness.yml
deleted file mode 100644
index d9603e8..0000000
--- a/.github/helper/semgrep_rules/frappe_correctness.yml
+++ /dev/null
@@ -1,133 +0,0 @@
-# This file specifies rules for correctness according to how frappe doctype data model works.
-
-rules:
-- id: frappe-modifying-but-not-comitting
- patterns:
- - pattern: |
- def $METHOD(self, ...):
- ...
- self.$ATTR = ...
- - pattern-not: |
- def $METHOD(self, ...):
- ...
- self.$ATTR = ...
- ...
- self.db_set(..., self.$ATTR, ...)
- - pattern-not: |
- def $METHOD(self, ...):
- ...
- self.$ATTR = $SOME_VAR
- ...
- self.db_set(..., $SOME_VAR, ...)
- - pattern-not: |
- def $METHOD(self, ...):
- ...
- self.$ATTR = $SOME_VAR
- ...
- self.save()
- - metavariable-regex:
- metavariable: '$ATTR'
- # this is negative look-ahead, add more attrs to ignore like (ignore|ignore_this_too|ignore_me)
- regex: '^(?!ignore_linked_doctypes|status_updater)(.*)$'
- - metavariable-regex:
- metavariable: "$METHOD"
- regex: "(on_submit|on_cancel)"
- message: |
- DocType modified in self.$METHOD. Please check if modification of self.$ATTR is commited to database.
- languages: [python]
- severity: ERROR
-
-- id: frappe-modifying-but-not-comitting-other-method
- patterns:
- - pattern: |
- class $DOCTYPE(...):
- def $METHOD(self, ...):
- ...
- self.$ANOTHER_METHOD()
- ...
-
- def $ANOTHER_METHOD(self, ...):
- ...
- self.$ATTR = ...
- - pattern-not: |
- class $DOCTYPE(...):
- def $METHOD(self, ...):
- ...
- self.$ANOTHER_METHOD()
- ...
-
- def $ANOTHER_METHOD(self, ...):
- ...
- self.$ATTR = ...
- ...
- self.db_set(..., self.$ATTR, ...)
- - pattern-not: |
- class $DOCTYPE(...):
- def $METHOD(self, ...):
- ...
- self.$ANOTHER_METHOD()
- ...
-
- def $ANOTHER_METHOD(self, ...):
- ...
- self.$ATTR = $SOME_VAR
- ...
- self.db_set(..., $SOME_VAR, ...)
- - pattern-not: |
- class $DOCTYPE(...):
- def $METHOD(self, ...):
- ...
- self.$ANOTHER_METHOD()
- ...
- self.save()
- def $ANOTHER_METHOD(self, ...):
- ...
- self.$ATTR = ...
- - metavariable-regex:
- metavariable: "$METHOD"
- regex: "(on_submit|on_cancel)"
- message: |
- self.$ANOTHER_METHOD is called from self.$METHOD, check if changes to self.$ATTR are commited to database.
- languages: [python]
- severity: ERROR
-
-- id: frappe-print-function-in-doctypes
- pattern: print(...)
- message: |
- Did you mean to leave this print statement in? Consider using msgprint or logger instead of print statement.
- languages: [python]
- severity: WARNING
- paths:
- include:
- - "*/**/doctype/*"
-
-- id: frappe-modifying-child-tables-while-iterating
- pattern-either:
- - pattern: |
- for $ROW in self.$TABLE:
- ...
- self.remove(...)
- - pattern: |
- for $ROW in self.$TABLE:
- ...
- self.append(...)
- message: |
- Child table being modified while iterating on it.
- languages: [python]
- severity: ERROR
- paths:
- include:
- - "*/**/doctype/*"
-
-- id: frappe-same-key-assigned-twice
- pattern-either:
- - pattern: |
- {..., $X: $A, ..., $X: $B, ...}
- - pattern: |
- dict(..., ($X, $A), ..., ($X, $B), ...)
- - pattern: |
- _dict(..., ($X, $A), ..., ($X, $B), ...)
- message: |
- key `$X` is uselessly assigned twice. This could be a potential bug.
- languages: [python]
- severity: ERROR
diff --git a/.github/helper/semgrep_rules/security.py b/.github/helper/semgrep_rules/security.py
deleted file mode 100644
index f477d7c..0000000
--- a/.github/helper/semgrep_rules/security.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def function_name(input):
- # ruleid: frappe-codeinjection-eval
- eval(input)
-
-# ok: frappe-codeinjection-eval
-eval("1 + 1")
diff --git a/.github/helper/semgrep_rules/security.yml b/.github/helper/semgrep_rules/security.yml
deleted file mode 100644
index 8b21979..0000000
--- a/.github/helper/semgrep_rules/security.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-rules:
-- id: frappe-codeinjection-eval
- patterns:
- - pattern-not: eval("...")
- - pattern: eval(...)
- message: |
- Detected the use of eval(). eval() can be dangerous if used to evaluate
- dynamic content. Avoid it or use safe_eval().
- languages: [python]
- severity: ERROR
diff --git a/.github/helper/semgrep_rules/translate.js b/.github/helper/semgrep_rules/translate.js
deleted file mode 100644
index 9cdfb75..0000000
--- a/.github/helper/semgrep_rules/translate.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// ruleid: frappe-translation-empty-string
-__("")
-// ruleid: frappe-translation-empty-string
-__('')
-
-// ok: frappe-translation-js-formatting
-__('Welcome {0}, get started with ERPNext in just a few clicks.', [full_name]);
-
-// ruleid: frappe-translation-js-formatting
-__(`Welcome ${full_name}, get started with ERPNext in just a few clicks.`);
-
-// ok: frappe-translation-js-formatting
-__('This is fine');
-
-
-// ok: frappe-translation-trailing-spaces
-__('This is fine');
-
-// ruleid: frappe-translation-trailing-spaces
-__(' this is not ok ');
-// ruleid: frappe-translation-trailing-spaces
-__('this is not ok ');
-// ruleid: frappe-translation-trailing-spaces
-__(' this is not ok');
-
-// ok: frappe-translation-js-splitting
-__('You have {0} subscribers in your mailing list.', [subscribers.length])
-
-// todoruleid: frappe-translation-js-splitting
-__('You have') + subscribers.length + __('subscribers in your mailing list.')
-
-// ruleid: frappe-translation-js-splitting
-__('You have' + 'subscribers in your mailing list.')
-
-// ruleid: frappe-translation-js-splitting
-__('You have {0} subscribers' +
- 'in your mailing list', [subscribers.length])
-
-// ok: frappe-translation-js-splitting
-__("Ctrl+Enter to add comment")
-
-// ruleid: frappe-translation-js-splitting
-__('You have {0} subscribers \
- in your mailing list', [subscribers.length])
diff --git a/.github/helper/semgrep_rules/translate.py b/.github/helper/semgrep_rules/translate.py
deleted file mode 100644
index 9de6aa9..0000000
--- a/.github/helper/semgrep_rules/translate.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Examples taken from https://frappeframework.com/docs/user/en/translations
-# This file is used for testing the tests.
-
-from frappe import _
-
-full_name = "Jon Doe"
-# ok: frappe-translation-python-formatting
-_('Welcome {0}, get started with ERPNext in just a few clicks.').format(full_name)
-
-# ruleid: frappe-translation-python-formatting
-_('Welcome %s, get started with ERPNext in just a few clicks.' % full_name)
-# ruleid: frappe-translation-python-formatting
-_('Welcome %(name)s, get started with ERPNext in just a few clicks.' % {'name': full_name})
-
-# ruleid: frappe-translation-python-formatting
-_('Welcome {0}, get started with ERPNext in just a few clicks.'.format(full_name))
-
-
-subscribers = ["Jon", "Doe"]
-# ok: frappe-translation-python-formatting
-_('You have {0} subscribers in your mailing list.').format(len(subscribers))
-
-# ruleid: frappe-translation-python-splitting
-_('You have') + len(subscribers) + _('subscribers in your mailing list.')
-
-# ruleid: frappe-translation-python-splitting
-_('You have {0} subscribers \
- in your mailing list').format(len(subscribers))
-
-# ok: frappe-translation-python-splitting
-_('You have {0} subscribers') \
- + 'in your mailing list'
-
-# ruleid: frappe-translation-trailing-spaces
-msg = _(" You have {0} pending invoice ")
-# ruleid: frappe-translation-trailing-spaces
-msg = _("You have {0} pending invoice ")
-# ruleid: frappe-translation-trailing-spaces
-msg = _(" You have {0} pending invoice")
-
-# ok: frappe-translation-trailing-spaces
-msg = ' ' + _("You have {0} pending invoices") + ' '
-
-# ruleid: frappe-translation-python-formatting
-_(f"can not format like this - {subscribers}")
-# ruleid: frappe-translation-python-splitting
-_(f"what" + f"this is also not cool")
-
-
-# ruleid: frappe-translation-empty-string
-_("")
-# ruleid: frappe-translation-empty-string
-_('')
-
-
-class Test:
- # ok: frappe-translation-python-splitting
- def __init__(
- args
- ):
- pass
diff --git a/.github/helper/semgrep_rules/translate.yml b/.github/helper/semgrep_rules/translate.yml
deleted file mode 100644
index 5f03fb9..0000000
--- a/.github/helper/semgrep_rules/translate.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-rules:
-- id: frappe-translation-empty-string
- pattern-either:
- - pattern: _("")
- - pattern: __("")
- message: |
- Empty string is useless for translation.
- Please refer: https://frappeframework.com/docs/user/en/translations
- languages: [python, javascript, json]
- severity: ERROR
-
-- id: frappe-translation-trailing-spaces
- pattern-either:
- - pattern: _("=~/(^[ \t]+|[ \t]+$)/")
- - pattern: __("=~/(^[ \t]+|[ \t]+$)/")
- message: |
- Trailing or leading whitespace not allowed in translate strings.
- Please refer: https://frappeframework.com/docs/user/en/translations
- languages: [python, javascript, json]
- severity: ERROR
-
-- id: frappe-translation-python-formatting
- pattern-either:
- - pattern: _("..." % ...)
- - pattern: _("...".format(...))
- - pattern: _(f"...")
- message: |
- Only positional formatters are allowed and formatting should not be done before translating.
- Please refer: https://frappeframework.com/docs/user/en/translations
- languages: [python]
- severity: ERROR
-
-- id: frappe-translation-js-formatting
- patterns:
- - pattern: __(`...`)
- - pattern-not: __("...")
- message: |
- Template strings are not allowed for text formatting.
- Please refer: https://frappeframework.com/docs/user/en/translations
- languages: [javascript, json]
- severity: ERROR
-
-- id: frappe-translation-python-splitting
- pattern-either:
- - pattern: _(...) + _(...)
- - pattern: _("..." + "...")
- - pattern-regex: '[\s\.]_\([^\)]*\\\s*' # lines broken by `\`
- - pattern-regex: '[\s\.]_\(\s*\n' # line breaks allowed by python for using ( )
- message: |
- Do not split strings inside translate function. Do not concatenate using translate functions.
- Please refer: https://frappeframework.com/docs/user/en/translations
- languages: [python]
- severity: ERROR
-
-- id: frappe-translation-js-splitting
- pattern-either:
- - pattern-regex: '__\([^\)]*[\\]\s+'
- - pattern: __('...' + '...', ...)
- - pattern: __('...') + __('...')
- message: |
- Do not split strings inside translate function. Do not concatenate using translate functions.
- Please refer: https://frappeframework.com/docs/user/en/translations
- languages: [javascript, json]
- severity: ERROR
diff --git a/.github/helper/semgrep_rules/ux.js b/.github/helper/semgrep_rules/ux.js
deleted file mode 100644
index ae73f9c..0000000
--- a/.github/helper/semgrep_rules/ux.js
+++ /dev/null
@@ -1,9 +0,0 @@
-
-// ok: frappe-missing-translate-function-js
-frappe.msgprint('{{ _("Both login and password required") }}');
-
-// ruleid: frappe-missing-translate-function-js
-frappe.msgprint('What');
-
-// ok: frappe-missing-translate-function-js
-frappe.throw(' {{ _("Both login and password required") }}. ');
diff --git a/.github/helper/semgrep_rules/ux.py b/.github/helper/semgrep_rules/ux.py
deleted file mode 100644
index a00d3cd..0000000
--- a/.github/helper/semgrep_rules/ux.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import frappe
-from frappe import msgprint, throw, _
-
-
-# ruleid: frappe-missing-translate-function-python
-throw("Error Occured")
-
-# ruleid: frappe-missing-translate-function-python
-frappe.throw("Error Occured")
-
-# ruleid: frappe-missing-translate-function-python
-frappe.msgprint("Useful message")
-
-# ruleid: frappe-missing-translate-function-python
-msgprint("Useful message")
-
-
-# ok: frappe-missing-translate-function-python
-translatedmessage = _("Hello")
-
-# ok: frappe-missing-translate-function-python
-throw(translatedmessage)
-
-# ok: frappe-missing-translate-function-python
-msgprint(translatedmessage)
-
-# ok: frappe-missing-translate-function-python
-msgprint(_("Helpful message"))
-
-# ok: frappe-missing-translate-function-python
-frappe.throw(_("Error occured"))
diff --git a/.github/helper/semgrep_rules/ux.yml b/.github/helper/semgrep_rules/ux.yml
deleted file mode 100644
index dd667f3..0000000
--- a/.github/helper/semgrep_rules/ux.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-rules:
-- id: frappe-missing-translate-function-python
- pattern-either:
- - patterns:
- - pattern: frappe.msgprint("...", ...)
- - pattern-not: frappe.msgprint(_("..."), ...)
- - patterns:
- - pattern: frappe.throw("...", ...)
- - pattern-not: frappe.throw(_("..."), ...)
- message: |
- All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
- languages: [python]
- severity: ERROR
-
-- id: frappe-missing-translate-function-js
- pattern-either:
- - patterns:
- - pattern: frappe.msgprint("...", ...)
- - pattern-not: frappe.msgprint(__("..."), ...)
- # ignore microtemplating e.g. msgprint("{{ _("server side translation") }}")
- - pattern-not: frappe.msgprint("=~/\{\{.*\_.*\}\}/i", ...)
- - patterns:
- - pattern: frappe.throw("...", ...)
- - pattern-not: frappe.throw(__("..."), ...)
- # ignore microtemplating
- - pattern-not: frappe.throw("=~/\{\{.*\_.*\}\}/i", ...)
- message: |
- All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
- languages: [javascript]
- severity: ERROR
diff --git a/.github/stale.yml b/.github/stale.yml
index 9322ae8..8b7cb9b 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,34 +1,36 @@
# Configuration for probot-stale - https://github.com/probot/stale
-# Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 15
+# Label to use when marking as stale
+staleLabel: inactive
-# Number of days of inactivity before a stale Issue or Pull Request is closed.
-# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 3
-
-# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
-exemptLabels:
- - hotfix
+# Limit the number of actions per hour, from 1-30. Default is 30
+limitPerRun: 10
# Set to true to ignore issues in a project (defaults to false)
-exemptProjects: false
+exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
-# Label to use when marking as stale
-staleLabel: inactive
+pulls:
+ daysUntilStale: 15
+ daysUntilClose: 3
+ exemptLabels:
+ - hotfix
+ markComment: >
+ This pull request has been automatically marked as inactive because it has
+ not had recent activity. It will be closed within 3 days if no further
+ activity occurs, but it only takes a comment to keep a contribution alive
+ :) Also, even if it is closed, you can always reopen the PR when you're
+ ready. Thank you for contributing.
-# Comment to post when marking as stale. Set to `false` to disable
-markComment: >
- This pull request has been automatically marked as stale because it has not had
- recent activity. It will be closed within a week if no further activity occurs, but it
- only takes a comment to keep a contribution alive :) Also, even if it is closed,
- you can always reopen the PR when you're ready. Thank you for contributing.
-
-# Limit the number of actions per hour, from 1-30. Default is 30
-limitPerRun: 30
-
-# Limit to only `issues` or `pulls`
-only: pulls
+issues:
+ daysUntilStale: 60
+ daysUntilClose: 7
+ exemptLabels:
+ - valid
+ - to-validate
+ markComment: >
+ This issue has been automatically marked as inactive because it has not had
+ recent activity and it wasn't validated by maintainer team. It will be
+ closed within a week if no further activity occurs.
diff --git a/.github/try-on-f-cloud-button.svg b/.github/try-on-f-cloud-button.svg
new file mode 100644
index 0000000..fe0bb2c
--- /dev/null
+++ b/.github/try-on-f-cloud-button.svg
@@ -0,0 +1,32 @@
+<svg width="201" height="60" viewBox="0 0 201 60" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g filter="url(#filter0_dd)">
+<rect x="4" y="2" width="193" height="52" rx="6" fill="#2490EF"/>
+<path d="M28 22.2891H32.8786V35.5H36.2088V22.2891H41.0874V19.5H28V22.2891Z" fill="white"/>
+<path d="M41.6982 35.5H45.0129V28.7109C45.0129 27.2344 46.0866 26.2188 47.5494 26.2188C48.0085 26.2188 48.6388 26.2969 48.95 26.3984V23.4453C48.6543 23.375 48.2419 23.3281 47.9074 23.3281C46.5691 23.3281 45.472 24.1094 45.0362 25.5938H44.9117V23.5H41.6982V35.5Z" fill="white"/>
+<path d="M52.8331 40C55.2996 40 56.6068 38.7344 57.2837 36.7969L61.9289 23.5156L58.4197 23.5L55.9221 32.3125H55.7976L53.3233 23.5H49.8374L54.1247 35.8437L53.9302 36.3516C53.4944 37.4766 52.6619 37.5312 51.4947 37.1719L50.7478 39.6562C51.2224 39.8594 51.9927 40 52.8331 40Z" fill="white"/>
+<path d="M73.6142 35.7344C77.2401 35.7344 79.4966 33.2422 79.4966 29.5469C79.4966 25.8281 77.2401 23.3438 73.6142 23.3438C69.9883 23.3438 67.7319 25.8281 67.7319 29.5469C67.7319 33.2422 69.9883 35.7344 73.6142 35.7344ZM73.6298 33.1562C71.9569 33.1562 71.101 31.6171 71.101 29.5233C71.101 27.4296 71.9569 25.8827 73.6298 25.8827C75.2715 25.8827 76.1274 27.4296 76.1274 29.5233C76.1274 31.6171 75.2715 33.1562 73.6298 33.1562Z" fill="white"/>
+<path d="M84.7253 28.5625C84.7331 27.0156 85.6512 26.1094 86.9895 26.1094C88.3201 26.1094 89.1215 26.9844 89.1137 28.4531V35.5H92.4284V27.8594C92.4284 25.0625 90.7945 23.3438 88.3046 23.3438C86.5306 23.3438 85.2466 24.2187 84.7097 25.6172H84.5697V23.5H81.4106V35.5H84.7253V28.5625Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M102.429 19.5H113.429V22.3141H102.429V19.5ZM102.429 35.5V26.6794H112.699V29.4982H105.94V35.5H102.429Z" fill="white"/>
+<path d="M131.584 24.9625C131.09 21.5057 128.345 19.5 124.785 19.5C120.589 19.5 117.429 22.463 117.429 27.4924C117.429 32.5142 120.55 35.4848 124.785 35.4848C128.604 35.4848 131.137 33.0916 131.584 30.1211L128.651 30.1059C128.282 31.9293 126.745 32.9549 124.824 32.9549C122.22 32.9549 120.354 31.0632 120.354 27.4924C120.354 23.9824 122.204 22.0299 124.832 22.0299C126.784 22.0299 128.314 23.1011 128.651 24.9625H131.584Z" fill="white"/>
+<path d="M136.409 19.7124H133.571V35.2718H136.409V19.7124Z" fill="white"/>
+<path d="M144.031 35.5001C147.56 35.5001 149.803 33.0917 149.803 29.483C149.803 25.8667 147.56 23.4507 144.031 23.4507C140.502 23.4507 138.259 25.8667 138.259 29.483C138.259 33.0917 140.502 35.5001 144.031 35.5001ZM144.047 33.2969C142.094 33.2969 141.137 31.6103 141.137 29.4754C141.137 27.3406 142.094 25.6312 144.047 25.6312C145.968 25.6312 146.925 27.3406 146.925 29.4754C146.925 31.6103 145.968 33.2969 144.047 33.2969Z" fill="white"/>
+<path d="M159.338 30.3641C159.338 32.1419 158.028 33.0232 156.773 33.0232C155.409 33.0232 154.499 32.0887 154.499 30.6072V23.6025H151.66V31.0327C151.66 33.8361 153.307 35.4239 155.675 35.4239C157.479 35.4239 158.749 34.5046 159.298 33.1979H159.424V35.272H162.176V23.6025H159.338V30.3641Z" fill="white"/>
+<path d="M169.014 35.4769C171.084 35.4769 172.017 34.2841 172.464 33.4332H172.637V35.2718H175.429V19.7124H172.582V25.532H172.464C172.033 24.6887 171.147 23.4503 169.022 23.4503C166.238 23.4503 164.05 25.5624 164.05 29.4522C164.05 33.2965 166.175 35.4769 169.014 35.4769ZM169.806 33.2205C167.931 33.2205 166.943 31.6251 166.943 29.437C166.943 27.2642 167.916 25.7067 169.806 25.7067C171.633 25.7067 172.637 27.173 172.637 29.437C172.637 31.701 171.617 33.2205 169.806 33.2205Z" fill="white"/>
+</g>
+<defs>
+<filter id="filter0_dd" x="0" y="0" width="201" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="0.25"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset dy="2"/>
+<feGaussianBlur stdDeviation="2"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/>
+<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
+</filter>
+</defs>
+</svg>
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
index 4b1147e..5b607a9 100644
--- a/.github/workflows/docker-release.yml
+++ b/.github/workflows/docker-release.yml
@@ -11,4 +11,4 @@
- name: curl
run: |
apk add curl bash
- curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests
+ curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}'
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
new file mode 100644
index 0000000..ebb88c9
--- /dev/null
+++ b/.github/workflows/linters.yml
@@ -0,0 +1,31 @@
+name: Linters
+
+on:
+ pull_request: { }
+
+jobs:
+
+ linters:
+ name: linters
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - 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
+
+ - name: Download Semgrep rules
+ run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
+
+ - uses: returntocorp/semgrep-action@v1
+ env:
+ SEMGREP_TIMEOUT: 120
+ with:
+ config: >-
+ r/python.lang.correctness
+ ./frappe-semgrep-rules/rules
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index 72d4028..97bccf5 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
@@ -77,4 +86,27 @@
cd ~/frappe-bench/
wget https://erpnext.com/files/v10-erpnext.sql.gz
bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz
+
+ git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
+ git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
+
+ for version in $(seq 12 13)
+ do
+ echo "Updating to v$version"
+ branch_name="version-$version-hotfix"
+
+ git -C "apps/frappe" fetch --depth 1 upstream $branch_name:$branch_name
+ git -C "apps/erpnext" fetch --depth 1 upstream $branch_name:$branch_name
+
+ git -C "apps/frappe" checkout -q -f $branch_name
+ git -C "apps/erpnext" checkout -q -f $branch_name
+
+ bench setup requirements --python
+ bench --site test_site migrate
+ done
+
+
+ echo "Updating to latest version"
+ git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
+ git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
bench --site test_site migrate
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..77c0aee 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
@@ -81,6 +91,8 @@
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
+ env:
+ TYPE: server
- name: Run Tests
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage
@@ -89,34 +101,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 0be9bd8..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,11 +99,13 @@
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
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..9609353 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,8 @@
[](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml)
[](https://www.codetriage.com/frappe/erpnext)
-[](https://coveralls.io/github/frappe/erpnext?branch=develop)
+[](https://codecov.io/gh/frappe/erpnext)
+[](https://hub.docker.com/r/frappe/erpnext-worker)
[https://erpnext.com](https://erpnext.com)
@@ -39,6 +40,12 @@
---
+<div align="center">
+ <a href="https://frappecloud.com/deploy?apps=frappe,erpnext&source=erpnext_readme">
+ <img src=".github/try-on-f-cloud-button.svg" height="40">
+ </a>
+</div>
+
### Containerized Installation
Use docker to deploy ERPNext in production or for development of [Frappe](https://github.com/frappe/frappe) apps. See https://github.com/frappe/frappe_docker for more details.
@@ -49,14 +56,6 @@
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
-### Virtual Image
-
-You can download a virtual image to run ERPNext in a virtual machine on your local system.
-
-- [ERPNext Download](http://erpnext.com/download)
-
-System and user credentials are listed on the download page.
-
---
## License
@@ -77,6 +76,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
index 820a23a..464cce4 100644
--- a/cypress/integration/test_organizational_chart_desktop.js
+++ b/cypress/integration/test_organizational_chart_desktop.js
@@ -2,8 +2,11 @@
before(() => {
cy.login();
cy.visit('/app/website');
- cy.awesomebar('Organizational Chart');
- cy.wait(500);
+ });
+
+ 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 => {
@@ -21,7 +24,7 @@
cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
cy.get('@input')
.clear({ force: true })
- .type('Test Org Chart{enter}', { force: true })
+ .type('Test Org Chart{downarrow}{enter}', { force: true })
.blur({ force: true });
});
});
diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js
index df90dbf..971ac6d 100644
--- a/cypress/integration/test_organizational_chart_mobile.js
+++ b/cypress/integration/test_organizational_chart_mobile.js
@@ -1,9 +1,14 @@
context('Organizational Chart Mobile', () => {
before(() => {
cy.login();
- cy.viewport(375, 667);
cy.visit('/app/website');
- cy.awesomebar('Organizational Chart');
+ });
+
+ 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({
@@ -20,7 +25,7 @@
cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
cy.get('@input')
.clear({ force: true })
- .type('Test Org Chart{enter}', { force: true })
+ .type('Test Org Chart{downarrow}{enter}', { force: true })
.blur({ force: true });
});
});
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 17d6505..a5de50f 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -1,11 +1,10 @@
-# -*- 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.9.0'
+import frappe
+
+from erpnext.hooks import regional_overrides
+
+__version__ = '14.0.0-dev'
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..551048e 100644
--- a/erpnext/accounts/custom/address.py
+++ b/erpnext/accounts/custom/address.py
@@ -1,11 +1,16 @@
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):
self.validate_reference()
+ self.update_compnay_address()
super(ERPNextAddress, self).validate()
def link_address(self):
@@ -15,6 +20,11 @@
return super(ERPNextAddress, self).link_address()
+ def update_compnay_address(self):
+ for link in self.get('links'):
+ if link.link_doctype == 'Company':
+ self.is_your_company_address = 1
+
def validate_reference(self):
if self.is_your_company_address and not [
row for row in self.links if row.link_doctype == "Company"
@@ -22,6 +32,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.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
index 85f54f9..1c1364e 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
@@ -1,15 +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, 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 0c81d83..22c81dd 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -1,12 +1,25 @@
-from __future__ import unicode_literals
-
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 +372,16 @@
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)
-
- frappe.flags.deferred_accounting_error = True
+ except Exception as e:
+ if frappe.flags.in_test:
+ traceback = frappe.get_traceback()
+ frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
+ raise e
+ else:
+ frappe.db.rollback()
+ traceback = frappe.get_traceback()
+ frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
+ frappe.flags.deferred_accounting_error = True
def send_mail(deferred_process):
title = _("Error while processing deferred accounting for {0}").format(deferred_process)
@@ -430,10 +447,12 @@
if submit:
journal_entry.submit()
- except:
+
+ frappe.db.commit()
+ except Exception:
frappe.db.rollback()
traceback = frappe.get_traceback()
- frappe.log_error(message=traceback)
+ frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
frappe.flags.deferred_accounting_error = True
diff --git a/erpnext/accounts/doctype/__init__.py b/erpnext/accounts/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/__init__.py
+++ b/erpnext/accounts/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/account/__init__.py b/erpnext/accounts/doctype/account/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/account/__init__.py
+++ b/erpnext/accounts/doctype/account/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..f8a06c7 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.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
+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 +197,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 +208,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..a3ef384 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -45,6 +45,50 @@
],
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) {
+ node.parent && node.parent.find('.balance-area').remove();
+ $('<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 +166,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"),
@@ -150,7 +176,7 @@
&& node.expandable && !node.hide_add;
},
click: function() {
- var me = frappe.treeview_settings['Account'].treeview;
+ var me = frappe.views.trees['Account'];
me.new_node();
},
btnClass: "hidden-xs"
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..a8de06c 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
@@ -1,25 +1,26 @@
# 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 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 unidecode import unidecode
-def create_charts(company, chart_template=None, existing_company=None, custom_chart=None):
+
+def create_charts(company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None):
chart = custom_chart or get_chart(chart_template, existing_company)
if chart:
accounts = []
def _import_accounts(children, parent, root_type, root_account=False):
- for account_name, child in iteritems(children):
+ for account_name, child in children.items():
if root_account:
root_type = child.get("root_type")
- if account_name not in ["account_number", "account_type",
+ if account_name not in ["account_name", "account_number", "account_type",
"root_type", "is_group", "tax_rate"]:
account_number = cstr(child.get("account_number")).strip()
@@ -32,7 +33,7 @@
account = frappe.get_doc({
"doctype": "Account",
- "account_name": account_name,
+ "account_name": child.get('account_name') if from_coa_importer else account_name,
"company": company,
"parent_account": parent,
"is_group": is_group,
@@ -78,7 +79,7 @@
def identify_is_group(child):
if child.get("is_group"):
is_group = child.get("is_group")
- elif len(set(child.keys()) - set(["account_type", "root_type", "is_group", "tax_rate", "account_number"])):
+ elif len(set(child.keys()) - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])):
is_group = 1
else:
is_group = 0
@@ -91,11 +92,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",)
@@ -195,7 +199,7 @@
if chart:
def _get_account_names(account_master):
- for account_name, child in iteritems(account_master):
+ for account_name, child in account_master.items():
if account_name not in ["account_number", "account_type",
"root_type", "is_group", "tax_rate"]:
accounts.append(account_name)
@@ -207,7 +211,7 @@
return (bank_account in accounts)
@frappe.whitelist()
-def build_tree_from_json(chart_template, chart_data=None):
+def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
''' get chart template from its folder and parse the json to be rendered as tree '''
chart = chart_data or get_chart(chart_template)
@@ -218,11 +222,14 @@
accounts = []
def _import_accounts(children, parent):
''' recursively called to form a parent-child based list of dict from chart template '''
- for account_name, child in iteritems(children):
+ for account_name, child in children.items():
account = {}
- if account_name in ["account_number", "account_type",\
+ if account_name in ["account_name", "account_number", "account_type",\
"root_type", "is_group", "tax_rate"]: continue
+ if from_coa_importer:
+ account_name = child['account_name']
+
account['parent_account'] = parent
account['expandable'] = True if identify_is_group(child) else False
account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py b/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py
index eb3e7ff..79001d7 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
@@ -4,15 +4,14 @@
"""
Import chart of accounts from OpenERP sources
"""
-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
-from six import iteritems
+import frappe
+from frappe.utils.csvutils import read_csv_content
path = "/Users/nabinhait/projects/odoo/addons"
@@ -139,7 +138,7 @@
def make_maps_for_xml(xml_roots, account_types, country_dir):
"""make maps for `charts` and `accounts`"""
- for model, root_list in iteritems(xml_roots):
+ for model, root_list in xml_roots.items():
for root in root_list:
for node in root[0].findall("record"):
if node.get("model")=="account.account.template":
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..9248ffa 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
@@ -1,9 +1,10 @@
# Copyright (c) 2015, 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():
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..31ae171 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
@@ -1,9 +1,10 @@
# Copyright (c) 2015, 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():
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..0715823 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -1,11 +1,14 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
import unittest
+
import frappe
-from 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.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index fac28c9..b6112e0 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -1,17 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 +46,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 4f3ee76..f781a22 100644
--- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+
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']
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..4b0cbb3 100644
--- a/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.py
+++ b/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 9dd882a..750e129 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js
@@ -8,7 +8,7 @@
}
let help_content =
- `<table class="table table-bordered" style="background-color: #f9f9f9;">
+ `<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
<tr><td>
<p>
<i class="fa fa-hand-right"></i>
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..7d32bad 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
@@ -1,12 +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 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..e2f85ba 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
@@ -1,12 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
+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 739d8f6..e294937 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -1,11 +1,11 @@
-# -*- 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
from frappe import _
+from frappe.model.document import Document
+
class OverlapError(frappe.ValidationError): pass
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..c06c2e0 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -1,14 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, add_months
-from erpnext.accounts.general_ledger import ClosedAccountingPeriod
+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 e44af3a..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.")
- }
-];
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index a246ae5..55ea571 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",
@@ -173,7 +174,7 @@
"default": "0",
"fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check",
- "label": "Automatically Fetch Payment Terms"
+ "label": "Automatically Fetch Payment Terms from Order"
},
{
"description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
@@ -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:04.335416",
+ "modified": "2021-10-11 17:42:36.427699",
"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 62c97f2..4839207 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_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 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
+
+from erpnext.stock.utils import check_pending_reposting
class AccountsSettings(Document):
@@ -19,9 +21,13 @@
frappe.db.set_default("add_taxes_from_item_tax_template",
self.get("add_taxes_from_item_tax_template", 0))
+ frappe.db.set_default("enable_common_party_accounting",
+ self.get("enable_common_party_accounting", 0))
+
self.validate_stale_days()
self.enable_payment_schedule_in_print()
self.toggle_discount_accounting_fields()
+ self.validate_pending_reposts()
def validate_stale_days(self):
if not self.allow_stale and cint(self.stale_days) <= 0:
@@ -53,3 +59,8 @@
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)
+
+
+ def validate_pending_reposts(self):
+ if self.acc_frozen_upto:
+ check_pending_reposting(self.acc_frozen_upto)
diff --git a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
index 014cf45..bf1e967 100644
--- a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
import unittest
import frappe
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/accounts/doctype/advance_tax/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__init__.py
copy to erpnext/accounts/doctype/advance_tax/__init__.py
diff --git a/erpnext/accounts/doctype/advance_tax/advance_tax.json b/erpnext/accounts/doctype/advance_tax/advance_tax.json
new file mode 100644
index 0000000..68706ab
--- /dev/null
+++ b/erpnext/accounts/doctype/advance_tax/advance_tax.json
@@ -0,0 +1,56 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2021-11-25 10:24:39.836195",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "reference_type",
+ "reference_name",
+ "reference_detail",
+ "account_head",
+ "allocated_amount"
+ ],
+ "fields": [
+ {
+ "fieldname": "reference_type",
+ "fieldtype": "Link",
+ "label": "Reference Type",
+ "options": "DocType"
+ },
+ {
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "label": "Reference Name",
+ "options": "reference_type"
+ },
+ {
+ "fieldname": "reference_detail",
+ "fieldtype": "Data",
+ "label": "Reference Detail"
+ },
+ {
+ "fieldname": "account_head",
+ "fieldtype": "Link",
+ "label": "Account Head",
+ "options": "Account"
+ },
+ {
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "label": "Allocated Amount",
+ "options": "party_account_currency"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-11-25 10:27:51.712286",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Advance Tax",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/advance_tax/advance_tax.py b/erpnext/accounts/doctype/advance_tax/advance_tax.py
new file mode 100644
index 0000000..2e784ef
--- /dev/null
+++ b/erpnext/accounts/doctype/advance_tax/advance_tax.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 AdvanceTax(Document):
+ pass
diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json
index 4d63499..05b284a 100644
--- a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json
+++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json
@@ -25,8 +25,7 @@
"allocated_amount",
"column_break_13",
"base_tax_amount",
- "base_total",
- "base_allocated_amount"
+ "base_total"
],
"fields": [
{
@@ -169,12 +168,6 @@
"options": "currency"
},
{
- "fieldname": "base_allocated_amount",
- "fieldtype": "Currency",
- "label": "Allocated Amount (Company Currency)",
- "options": "Company:company:default_currency"
- },
- {
"fetch_from": "account_head.account_currency",
"fieldname": "currency",
"fieldtype": "Link",
@@ -186,7 +179,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-09 11:46:58.373170",
+ "modified": "2021-11-25 11:10:10.945027",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Advance Taxes and Charges",
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..55c84fb 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..a3173a8 100644
--- a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py
+++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..a532070 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
@@ -1,9 +1,9 @@
-# -*- 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 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..aae2166 100644
--- a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py
+++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ApplicableOnAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/bank/bank.py b/erpnext/accounts/doctype/bank/bank.py
index 99fa21c..f111433 100644
--- a/erpnext/accounts/doctype/bank/bank.py
+++ b/erpnext/accounts/doctype/bank/bank.py
@@ -1,11 +1,13 @@
-# -*- 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.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):
diff --git a/erpnext/accounts/doctype/bank/bank_dashboard.py b/erpnext/accounts/doctype/bank/bank_dashboard.py
index 1e2383d..36482aa 100644
--- a/erpnext/accounts/doctype/bank/bank_dashboard.py
+++ b/erpnext/accounts/doctype/bank/bank_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..5ca0e99 100644
--- a/erpnext/accounts/doctype/bank/test_bank.py
+++ b/erpnext/accounts/doctype/bank/test_bank.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..f9140c3 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -1,12 +1,15 @@
-# -*- 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.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 c7ea152..db4d7e5 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..5f23f88 100644
--- a/erpnext/accounts/doctype/bank_account/test_bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-from frappe import _
-from frappe import ValidationError
-import unittest
+from frappe import ValidationError
# test_records = frappe.get_test_records('Bank Account')
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..6355478 100644
--- a/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.py
+++ b/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.py
@@ -1,9 +1,9 @@
-# -*- 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 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..a5faf1c 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..177b711 100644
--- a/erpnext/accounts/doctype/bank_account_type/bank_account_type.py
+++ b/erpnext/accounts/doctype/bank_account_type/bank_account_type.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..fee8b47 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
@@ -1,10 +1,9 @@
-# -*- 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 TestBankAccountType(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index 79f5596..a3bbb22 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -1,11 +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
-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..706fbbe 100644
--- a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestBankClearance(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/bank_clearance_detail/__init__.py b/erpnext/accounts/doctype/bank_clearance_detail/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/bank_clearance_detail/__init__.py
+++ b/erpnext/accounts/doctype/bank_clearance_detail/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 59299f8..3d29fd7 100644
--- a/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
+++ b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
@@ -1,9 +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 frappe
+
from frappe.model.document import Document
+
class BankClearanceDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
index a0aac6a..cfbcf16 100644
--- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
+++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
@@ -1,12 +1,14 @@
-# -*- 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, 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):
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..b992c6a 100644
--- a/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.py
+++ b/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..e7371fb 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -1,19 +1,21 @@
-# -*- 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 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):
@@ -340,7 +342,15 @@
def get_je_matching_query(amount_condition, transaction):
# get matching journal entry query
- cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
+
+ company_account = frappe.get_value("Bank Account", transaction.bank_account, "account")
+ root_type = frappe.get_value("Account", company_account, "root_type")
+
+ if root_type == "Liability":
+ cr_or_dr = "debit" if transaction.withdrawal > 0 else "credit"
+ else:
+ cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
+
return f"""
SELECT
@@ -424,7 +434,7 @@
def get_ec_matching_query(bank_account, company, amount_condition):
# get matching Expense Claim query
- mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account",
+ mode_of_payments = [x["parent"] for x in frappe.db.get_all("Mode of Payment Account",
filters={"default_account": bank_account}, fields=["parent"])]
mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
company_currency = get_company_currency(company)
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..599ced5 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
@@ -1,10 +1,9 @@
-# -*- 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 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..e786d13 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -1,24 +1,21 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import csv
import json
import re
-import openpyxl
-from openpyxl.styles import Font
-from openpyxl.utils import get_column_letter
-from six import string_types
-
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 handle_html, ILLEGAL_CHARACTERS_RE
-from frappe import _
+from frappe.utils.xlsxutils import ILLEGAL_CHARACTERS_RE, handle_html
+from openpyxl.styles import Font
+from openpyxl.utils import get_column_letter
-from frappe.core.doctype.data_import.data_import import DataImport
class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs):
@@ -181,12 +178,12 @@
for row in data:
clean_row = []
for item in row:
- if isinstance(item, string_types) and (sheet_name not in ['Data Import Template', 'Data Export']):
+ if isinstance(item, str) and (sheet_name not in ['Data Import Template', 'Data Export']):
value = handle_html(item)
else:
value = item
- if isinstance(item, string_types) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
+ if isinstance(item, str) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
# Remove illegal characters from the string
value = re.sub(ILLEGAL_CHARACTERS_RE, '', value)
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..08c12bd 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 7ea71fc..4620087 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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):
@@ -21,7 +21,7 @@
self.update_allocations()
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)
@@ -45,7 +45,7 @@
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
self.reload()
-
+
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"]:
@@ -77,7 +77,7 @@
def get_reconciled_bank_transactions(payment_entry):
reconciled_bank_transactions = frappe.get_all(
- 'Bank Transaction Payments',
+ 'Bank Transaction Payments',
filters = {
'payment_entry': payment_entry.payment_entry
},
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
index dc3b867..cca8a88 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
@@ -1,13 +1,13 @@
-# -*- 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
+
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():
@@ -42,7 +42,7 @@
if all(item is None for item in d) is True:
continue
fields = {}
- for key, value in iteritems(header_map):
+ for key, value in header_map.items():
fields.update({key: d[int(value)-1]})
try:
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 439d489..72b6893 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -1,16 +1,19 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import json
+import unittest
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
+
+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"]
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..e19712c 100644
--- a/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py
+++ b/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py
@@ -1,9 +1,9 @@
-# -*- 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 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..0536aa2 100644
--- a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py
+++ b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py
@@ -1,9 +1,9 @@
-# -*- 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 BankTransactionPayments(Document):
pass
diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py
index d93b6ff..492bb365 100644
--- a/erpnext/accounts/doctype/budget/budget.py
+++ b/erpnext/accounts/doctype/budget/budget.py
@@ -1,15 +1,18 @@
- # -*- 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..9a83a0a 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -1,15 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, now_datetime
+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..65bc951 100644
--- a/erpnext/accounts/doctype/budget_account/budget_account.py
+++ b/erpnext/accounts/doctype/budget_account/budget_account.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class BudgetAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/c_form/__init__.py b/erpnext/accounts/doctype/c_form/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/c_form/__init__.py
+++ b/erpnext/accounts/doctype/c_form/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py
index cfe28f3..61331d3 100644
--- a/erpnext/accounts/doctype/c_form/c_form.py
+++ b/erpnext/accounts/doctype/c_form/c_form.py
@@ -1,11 +1,12 @@
# 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 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..fa34c25 100644
--- a/erpnext/accounts/doctype/c_form/test_c_form.py
+++ b/erpnext/accounts/doctype/c_form/test_c_form.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/accounts/doctype/c_form_invoice_detail/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/c_form_invoice_detail/__init__.py
+++ b/erpnext/accounts/doctype/c_form_invoice_detail/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 20e423a..1e6ab97 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
@@ -1,9 +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 frappe
+
from frappe.model.document import Document
+
class CFormInvoiceDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/campaign_item/campaign_item.py b/erpnext/accounts/doctype/campaign_item/campaign_item.py
index 4f5fd7f..d78fdf5 100644
--- a/erpnext/accounts/doctype/campaign_item/campaign_item.py
+++ b/erpnext/accounts/doctype/campaign_item/campaign_item.py
@@ -4,5 +4,6 @@
# 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..d975f80 100644
--- a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py
+++ b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py
@@ -1,8 +1,7 @@
-# -*- 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
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
index 43ebcb0..6e7b687 100644
--- a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
+++ b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
DEFAULT_MAPPERS = [
{
'doctype': 'Cash Flow Mapper',
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_mapper/test_cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.py
index f055e56..044f2ae 100644
--- a/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.py
+++ b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
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 b1ad297..cd8381a 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
@@ -1,8 +1,7 @@
-# -*- 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
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..abb2567 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
class TestCashFlowMapping(unittest.TestCase):
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..d8dd05c 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
@@ -1,8 +1,7 @@
-# -*- 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
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..610428c 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
@@ -1,8 +1,7 @@
-# -*- 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
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/test_cash_flow_mapping_template.py b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.py
index d6b964b..1946146 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
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..d15ab7e 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
@@ -1,8 +1,7 @@
-# -*- 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
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/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.py b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.py
index db5683a..5795e61 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
index 081c6fa..9fbd0c9 100644
--- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
+++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# 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 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):
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..d11737c 100644
--- a/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.py
+++ b/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..7617f9b 100644
--- a/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.py
+++ b/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# 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.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..aaacce4 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
@@ -1,20 +1,41 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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,8 +65,9 @@
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)
+ create_charts(company, custom_chart=forest, from_coa_importer=True)
# trigger on_update for company to reset default accounts
set_default_accounts(company)
@@ -108,7 +130,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 +141,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, from_coa_importer=True) # 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):
'''
@@ -180,11 +210,14 @@
if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
+ name = account_name
if account_number:
account_number = cstr(account_number).strip()
account_name = "{} - {}".format(account_number, account_name)
charts_map[account_name] = {}
+ charts_map[account_name]['account_name'] = name
+ if account_number: charts_map[account_name]["account_number"] = account_number
if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
if account_type: charts_map[account_name]["account_type"] = account_type
if root_type: charts_map[account_name]["root_type"] = root_type
@@ -282,10 +315,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 +334,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 +346,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 +384,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..00e5cc3 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# 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.py b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
index d62ee9d..20cb42c 100644
--- a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
+++ b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
@@ -1,11 +1,11 @@
-# -*- 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.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..2b323a9 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
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..89d3d2e 100644
--- a/erpnext/accounts/doctype/closed_document/closed_document.py
+++ b/erpnext/accounts/doctype/closed_document/closed_document.py
@@ -1,10 +1,9 @@
-# -*- 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 ClosedDocument(Document):
pass
diff --git a/erpnext/accounts/doctype/cost_center/__init__.py b/erpnext/accounts/doctype/cost_center/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/cost_center/__init__.py
+++ b/erpnext/accounts/doctype/cost_center/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py
index 981fec3..7ae0a72 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center.py
@@ -1,11 +1,12 @@
# 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 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
diff --git a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
index 24cf3ea..f524803 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py
index 7779cce..f8615ec 100644
--- a/erpnext/accounts/doctype/cost_center/test_cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py
@@ -1,7 +1,8 @@
# 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')
diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py
index 92a816d..ee32de1 100644
--- a/erpnext/accounts/doctype/coupon_code/coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py
@@ -1,12 +1,13 @@
-# -*- 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.utils import (strip)
+from frappe.utils import strip
+
+
class CouponCode(Document):
def autoname(self):
self.coupon_name = strip(self.coupon_name)
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 06987a8..5ba0691 100644
--- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
@@ -1,13 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
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']
diff --git a/erpnext/accounts/doctype/customer_group_item/customer_group_item.py b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py
index df782ac..100bfd5 100644
--- a/erpnext/accounts/doctype/customer_group_item/customer_group_item.py
+++ b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class CustomerGroupItem(Document):
pass
diff --git a/erpnext/accounts/doctype/customer_item/customer_item.py b/erpnext/accounts/doctype/customer_item/customer_item.py
index a577145..da3533f 100644
--- a/erpnext/accounts/doctype/customer_item/customer_item.py
+++ b/erpnext/accounts/doctype/customer_item/customer_item.py
@@ -4,5 +4,6 @@
# 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 93dfcc1..9b8932c 100644
--- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
+++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 DiscountedInvoice(Document):
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..dcf0e3b 100644
--- a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py
+++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class DistributedCostCenter(Document):
pass
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index 1ef512a..5da0077 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -1,15 +1,16 @@
-# -*- 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 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
+
+import frappe
+from frappe.utils import cint, flt, getdate
+
+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
@@ -106,7 +107,7 @@
@frappe.whitelist()
def get_dunning_letter_text(dunning_type, doc, language=None):
- if isinstance(doc, string_types):
+ if isinstance(doc, str):
doc = json.loads(doc)
if language:
filters = {'parent': dunning_type, 'language': language}
diff --git a/erpnext/accounts/doctype/dunning/dunning_dashboard.py b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
index 33c6ab0..a891bd2 100644
--- a/erpnext/accounts/doctype/dunning/dunning_dashboard.py
+++ b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'dunning',
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index 67692ec..b043c5b 100644
--- a/erpnext/accounts/doctype/dunning/test_dunning.py
+++ b/erpnext/accounts/doctype/dunning/test_dunning.py
@@ -1,15 +1,19 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+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):
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..9f3cf7f 100644
--- a/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py
+++ b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..1b9bb9c 100644
--- a/erpnext/accounts/doctype/dunning_type/dunning_type.py
+++ b/erpnext/accounts/doctype/dunning_type/dunning_type.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..67b72e4 100644
--- a/erpnext/accounts/doctype/dunning_type/test_dunning_type.py
+++ b/erpnext/accounts/doctype/dunning_type/test_dunning_type.py
@@ -1,10 +1,9 @@
-# -*- 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 TestDunningType(unittest.TestCase):
pass
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 dbbcedc..1b13195 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -1,15 +1,17 @@
-# -*- 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, 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):
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..fe86250 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,3 @@
-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..ec55e60 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..96a92bb 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
@@ -1,10 +1,9 @@
-# -*- 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 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..78b321b 100644
--- a/erpnext/accounts/doctype/finance_book/finance_book.py
+++ b/erpnext/accounts/doctype/finance_book/finance_book.py
@@ -1,10 +1,9 @@
-# -*- 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 FinanceBook(Document):
pass
diff --git a/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py b/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
index c2ebea6..57b039d 100644
--- a/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
+++ b/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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 2ba2139..5fb3d0a 100644
--- a/erpnext/accounts/doctype/finance_book/test_finance_book.py
+++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+import unittest
import frappe
-import unittest
+
+from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+
class TestFinanceBook(unittest.TestCase):
def test_finance_book(self):
@@ -40,4 +40,4 @@
else:
finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
- return finance_book
\ No newline at end of file
+ return finance_book
diff --git a/erpnext/accounts/doctype/fiscal_year/__init__.py b/erpnext/accounts/doctype/fiscal_year/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/fiscal_year/__init__.py
+++ b/erpnext/accounts/doctype/fiscal_year/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index 4255626..dd893f9 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -1,13 +1,13 @@
# 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 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..892a2c6 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
@@ -13,7 +11,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..69e13a4 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -1,13 +1,14 @@
# 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
+import unittest
+
+import frappe
+from frappe.utils import now_datetime
from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate
-test_records = frappe.get_test_records('Fiscal Year')
test_ignore = ["Company"]
class TestFiscalYear(unittest.TestCase):
@@ -24,3 +25,29 @@
})
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
+
+
+def test_record_generator():
+ test_records = [
+ {
+ "doctype": "Fiscal Year",
+ "year": "_Test Short Fiscal Year 2011",
+ "is_short_year": 1,
+ "year_end_date": "2011-04-01",
+ "year_start_date": "2011-12-31"
+ }
+ ]
+
+ start = 2012
+ end = now_datetime().year + 5
+ for year in range(start, end):
+ test_records.append({
+ "doctype": "Fiscal Year",
+ "year": f"_Test Fiscal Year {year}",
+ "year_start_date": f"{year}-01-01",
+ "year_end_date": f"{year}-12-31"
+ })
+
+ return test_records
+
+test_records = test_record_generator()
diff --git a/erpnext/accounts/doctype/fiscal_year/test_records.json b/erpnext/accounts/doctype/fiscal_year/test_records.json
deleted file mode 100644
index 4405253..0000000
--- a/erpnext/accounts/doctype/fiscal_year/test_records.json
+++ /dev/null
@@ -1,69 +0,0 @@
-[
- {
- "doctype": "Fiscal Year",
- "year": "_Test Short Fiscal Year 2011",
- "is_short_year": 1,
- "year_end_date": "2011-04-01",
- "year_start_date": "2011-12-31"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2012",
- "year_end_date": "2012-12-31",
- "year_start_date": "2012-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2013",
- "year_end_date": "2013-12-31",
- "year_start_date": "2013-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2014",
- "year_end_date": "2014-12-31",
- "year_start_date": "2014-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2015",
- "year_end_date": "2015-12-31",
- "year_start_date": "2015-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2016",
- "year_end_date": "2016-12-31",
- "year_start_date": "2016-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2017",
- "year_end_date": "2017-12-31",
- "year_start_date": "2017-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2018",
- "year_end_date": "2018-12-31",
- "year_start_date": "2018-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2019",
- "year_end_date": "2019-12-31",
- "year_start_date": "2019-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2020",
- "year_end_date": "2020-12-31",
- "year_start_date": "2020-01-01"
- },
- {
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2021",
- "year_end_date": "2021-12-31",
- "year_start_date": "2021-01-01"
- }
-]
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..d5db78d 100644
--- a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py
+++ b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class FiscalYearCompany(Document):
pass
diff --git a/erpnext/accounts/doctype/gl_entry/__init__.py b/erpnext/accounts/doctype/gl_entry/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/gl_entry/__init__.py
+++ b/erpnext/accounts/doctype/gl_entry/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 0844995..9d1452b 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -1,20 +1,28 @@
# 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, 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 six import iteritems
+from frappe.model.naming import set_name_from_naming_options
+from frappe.utils import flt, fmt_money
+
+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):
@@ -48,7 +56,8 @@
# Update outstanding amt on against voucher
if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
- and self.against_voucher and self.flags.update_outstanding == 'Yes'):
+ and self.against_voucher and self.flags.update_outstanding == 'Yes'
+ and not frappe.flags.is_reverse_depr_entry):
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
self.against_voucher)
@@ -105,7 +114,7 @@
def validate_allowed_dimensions(self):
dimension_filter_map = get_dimension_filter_map()
- for key, value in iteritems(dimension_filter_map):
+ for key, value in dimension_filter_map.items():
dimension = key[0]
account = key[1]
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..3de2394 100644
--- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
@@ -1,11 +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, 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..befca41 100644
--- a/erpnext/accounts/doctype/gst_account/gst_account.py
+++ b/erpnext/accounts/doctype/gst_account/gst_account.py
@@ -1,10 +1,9 @@
-# -*- 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 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..09c389d 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -1,14 +1,20 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 6d35ca2..b748429 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'reference_name',
diff --git a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
index 919dd0c..d1d4be3 100644
--- a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
@@ -1,15 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+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..0ceb6a0 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
@@ -1,11 +1,11 @@
-# -*- 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
from frappe import _
+from frappe.model.document import Document
+
class ItemTaxTemplate(Document):
def validate(self):
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
index 3d80a97..af01c57 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..e8638bb 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..221081e 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
@@ -1,10 +1,9 @@
-# -*- 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 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 88e747b..ea9ac23 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -1,21 +1,32 @@
# 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, 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 erpnext.accounts.party import get_party_gle_currency
-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
+
+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, get_party_gle_currency
+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,9 +57,13 @@
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
- self.validate_against_jv()
+ if not frappe.flags.is_reverse_depr_entry:
+ self.validate_against_jv()
+ self.validate_stock_accounts()
+
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()
@@ -56,7 +71,10 @@
self.validate_empty_accounts_table()
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()
@@ -68,6 +86,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)
@@ -85,6 +104,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
@@ -96,10 +116,19 @@
if d.reference_type in ("Sales Order", "Purchase Order", "Employee Advance"):
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
- for voucher_type, order_list in iteritems(advance_paid):
+ for voucher_type, order_list in advance_paid.items():
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)
@@ -119,6 +148,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,\
@@ -196,14 +291,14 @@
if account_type in ["Receivable", "Payable"]:
if not (d.party_type and d.party):
frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account))
-
+
def validate_party_account_currency(self):
for d in self.get("accounts"):
if d.party_type in ('Customer', 'Supplier'):
party_gle_currency = get_party_gle_currency(d.party_type, d.party, self.company)
party_account_currency = get_account_currency(d.account)
party_currency = frappe.db.get_value(d.party_type, d.party, 'default_currency')
-
+
if not party_gle_currency and (party_account_currency != party_currency):
frappe.throw(_("Party Account {0} currency and default party currency should be same").format(frappe.bold(d.account)))
@@ -346,7 +441,7 @@
def validate_orders(self):
"""Validate totals, closed and docstatus for orders"""
- for reference_name, total in iteritems(self.reference_totals):
+ for reference_name, total in self.reference_totals.items():
reference_type = self.reference_types[reference_name]
account = self.reference_accounts[reference_name]
@@ -377,7 +472,7 @@
def validate_invoices(self):
"""Validate totals and docstatus for invoices"""
- for reference_name, total in iteritems(self.reference_totals):
+ for reference_name, total in self.reference_totals.items():
reference_type = self.reference_types[reference_name]
if (reference_type in ("Sales Invoice", "Purchase Invoice") and
@@ -655,7 +750,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):
@@ -919,7 +1017,7 @@
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
company_currency = erpnext.get_company_currency(args.get("company"))
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index 5835d46..481462b 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -1,12 +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 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):
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..534b589 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class JournalEntryAccount(Document):
pass
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..2da72c2 100644
--- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class JournalEntryTemplate(Document):
pass
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..868a0ee 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestJournalEntryTemplate(unittest.TestCase):
pass
diff --git a/erpnext/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..f84fddd 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class JournalEntryTemplateAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
index 3579a1a..f460b9f 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
@@ -1,8 +1,7 @@
-# -*- 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
from frappe.utils import today
@@ -15,7 +14,7 @@
def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=None):
if not expiry_date:
- date = today()
+ expiry_date = today()
return frappe.db.sql('''
select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
diff --git a/erpnext/accounts/doctype/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..cd38559 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..bc8f5c7 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
@@ -1,10 +1,9 @@
-# -*- 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 LoyaltyPointEntryRedemption(Document):
pass
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
index f90f867..6951b2a 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
@@ -6,7 +6,7 @@
frappe.ui.form.on('Loyalty Program', {
setup: function(frm) {
var help_content =
- `<table class="table table-bordered" style="background-color: #f9f9f9;">
+ `<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
<tr><td>
<h4>
<i class="fa fa-hand-right"></i>
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
index cb753a3..70da03b 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
@@ -1,13 +1,12 @@
-# -*- 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
+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..25328e5 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
@@ -1,8 +1,3 @@
-from __future__ import unicode_literals
-
-from frappe import _
-
-
def get_data():
return {
'fieldname': 'loyalty_program',
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..82c1432 100644
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -1,14 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+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..c462d5f 100644
--- a/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py
+++ b/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py
@@ -1,10 +1,9 @@
-# -*- 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 LoyaltyProgramCollection(Document):
pass
diff --git a/erpnext/accounts/doctype/mode_of_payment/__init__.py b/erpnext/accounts/doctype/mode_of_payment/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/mode_of_payment/__init__.py
+++ b/erpnext/accounts/doctype/mode_of_payment/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 cea921e..f21d1b9 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
@@ -1,11 +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
-from frappe.model.document import Document
+import frappe
from frappe import _
+from frappe.model.document import Document
+
class ModeofPayment(Document):
def validate(self):
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..2ff02a7 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
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..3d3bba6 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
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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 ad8623f..a8c5f68 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
@@ -1,11 +1,12 @@
# 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 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()
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
index 912bd9e..96008c4 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
index efbf4eb..4a878b2 100644
--- a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
@@ -1,10 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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..274e2b6 100644
--- a/erpnext/accounts/doctype/monthly_distribution_percentage/monthly_distribution_percentage.py
+++ b/erpnext/accounts/doctype/monthly_distribution_percentage/monthly_distribution_percentage.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class MonthlyDistributionPercentage(Document):
pass
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
index 9914b45..2a923f0 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
@@ -1,16 +1,19 @@
-# -*- 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
+
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):
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..c795e83 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
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..6c0ca4a 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
@@ -1,10 +1,9 @@
-# -*- 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.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..cd270b1 100644
--- a/erpnext/accounts/doctype/party_account/party_account.py
+++ b/erpnext/accounts/doctype/party_account/party_account.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PartyAccount(Document):
pass
diff --git a/erpnext/healthcare/doctype/body_part_link/__init__.py b/erpnext/accounts/doctype/party_link/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/body_part_link/__init__.py
rename 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..e9f813c
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/party_link.py
@@ -0,0 +1,41 @@
+# 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]))
+
+
+@frappe.whitelist()
+def create_party_link(primary_role, primary_party, secondary_party):
+ party_link = frappe.new_doc('Party Link')
+ party_link.primary_role = primary_role
+ party_link.primary_party = primary_party
+ party_link.secondary_role = 'Customer' if primary_role == 'Supplier' else 'Supplier'
+ party_link.secondary_party = secondary_party
+
+ party_link.save(ignore_permissions=True)
+
+ return party_link
+
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 d96bc27..3be3925 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -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.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 6f362c1..c8d1db9 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -27,10 +27,12 @@
"payment_accounts_section",
"party_balance",
"paid_from",
+ "paid_from_account_type",
"paid_from_account_currency",
"paid_from_account_balance",
"column_break_18",
"paid_to",
+ "paid_to_account_type",
"paid_to_account_currency",
"paid_to_account_balance",
"payment_amounts_section",
@@ -59,7 +61,6 @@
"taxes_and_charges_section",
"purchase_taxes_and_charges_template",
"sales_taxes_and_charges_template",
- "advance_tax_account",
"column_break_55",
"apply_tax_withholding_amount",
"tax_withholding_category",
@@ -440,7 +441,8 @@
"depends_on": "eval:(doc.paid_from && doc.paid_to)",
"fieldname": "reference_no",
"fieldtype": "Data",
- "label": "Cheque/Reference No"
+ "label": "Cheque/Reference No",
+ "mandatory_depends_on": "eval:(doc.paid_from_account_type == 'Bank' || doc.paid_to_account_type == 'Bank')"
},
{
"fieldname": "column_break_23",
@@ -452,6 +454,7 @@
"fieldname": "reference_date",
"fieldtype": "Date",
"label": "Cheque/Reference Date",
+ "mandatory_depends_on": "eval:(doc.paid_from_account_type == 'Bank' || doc.paid_to_account_type == 'Bank')",
"search_index": 1
},
{
@@ -682,15 +685,6 @@
"hide_border": 1
},
{
- "depends_on": "eval:doc.apply_tax_withholding_amount",
- "description": "Provisional tax account for advance tax. Taxes are parked in this account until payments are allocated to invoices",
- "fieldname": "advance_tax_account",
- "fieldtype": "Link",
- "label": "Advance Tax Account",
- "mandatory_depends_on": "eval:doc.apply_tax_withholding_amount",
- "options": "Account"
- },
- {
"depends_on": "eval:doc.received_amount && doc.payment_type != 'Internal Transfer'",
"fieldname": "received_amount_after_tax",
"fieldtype": "Currency",
@@ -707,15 +701,30 @@
"label": "Received Amount After Tax (Company Currency)",
"options": "Company:company:default_currency",
"read_only": 1
+ },
+ {
+ "fetch_from": "paid_from.account_type",
+ "fieldname": "paid_from_account_type",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Paid From Account Type"
+ },
+ {
+ "fetch_from": "paid_to.account_type",
+ "fieldname": "paid_to_account_type",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Paid To Account Type"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-07-09 08:58:15.008761",
+ "modified": "2021-11-24 18:58:24.919764",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index abacee9..c1b056b 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1,24 +1,36 @@
-# -*- 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, 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
+
+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, process_gl_map
+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
@@ -75,9 +87,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()
@@ -85,9 +97,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)
@@ -190,7 +202,7 @@
ref_details = get_reference_details(d.reference_doctype,
d.reference_name, self.party_account_currency)
- for field, value in iteritems(ref_details):
+ for field, value in ref_details.items():
if d.exchange_gain_loss:
# for cases where gain/loss is booked into invoice
# exchange_gain_loss is calculated from invoice & populated
@@ -327,7 +339,7 @@
for k, v in no_oustanding_refs.items():
frappe.msgprint(
_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.")
- .format(k, frappe.bold(", ".join(d.reference_name for d in v)), frappe.bold("negative outstanding amount"))
+ .format(_(k), frappe.bold(", ".join(d.reference_name for d in v)), frappe.bold(_("negative outstanding amount")))
+ "<br><br>" + _("If this is undesirable please cancel the corresponding Payment Entry."),
title=_("Warning"), indicator="orange")
@@ -374,7 +386,10 @@
invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding
invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
- for key, allocated_amount in iteritems(invoice_payment_amount_map):
+ for idx, (key, allocated_amount) in enumerate(invoice_payment_amount_map.items(), 1):
+ if not invoice_paid_amount_map.get(key):
+ frappe.throw(_('Payment term {0} not used in {1}').format(key[0], key[1]))
+
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get('discounted_amt'))
@@ -389,7 +404,7 @@
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
else:
if allocated_amount > outstanding:
- frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
+ frappe.throw(_('Row #{0}: Cannot allocate more than {1} against payment term {2}').format(idx, outstanding, key[0]))
if allocated_amount and outstanding:
frappe.db.sql("""
@@ -418,23 +433,12 @@
if not self.apply_tax_withholding_amount:
return
- if not self.advance_tax_account:
- frappe.throw(_("Advance TDS account is mandatory for advance TDS deduction"))
-
net_total = self.paid_amount
- for reference in self.get("references"):
- 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
-
# Adding args as purchase invoice to get TDS amount
args = frappe._dict({
'company': self.company,
- 'doctype': 'Purchase Invoice',
+ 'doctype': 'Payment Entry',
'supplier': self.party,
'posting_date': self.posting_date,
'net_total': net_total
@@ -446,7 +450,6 @@
return
tax_withholding_details.update({
- 'add_deduct_tax': 'Add',
'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company)
})
@@ -484,15 +487,16 @@
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:
+ if self.paid_amount < self.received_amount:
frappe.throw(_("Received Amount cannot be greater than Paid Amount"))
def set_received_amount(self):
self.base_received_amount = self.base_paid_amount
- if self.paid_from_account_currency == self.paid_to_account_currency:
+ if self.paid_from_account_currency == self.paid_to_account_currency \
+ and not self.payment_type == 'Internal Transfer':
self.received_amount = self.paid_amount
def set_amounts_after_tax(self):
@@ -607,7 +611,7 @@
if not total_negative_outstanding:
frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
- .format(self.payment_type, ("to" if self.party_type=="Customer" else "from"),
+ .format(_(self.payment_type), (_("to") if self.party_type=="Customer" else _("from")),
self.party_type), InvalidPaymentEntry)
elif paid_amount - additional_charges > total_negative_outstanding:
@@ -673,6 +677,7 @@
self.add_deductions_gl_entries(gl_entries)
self.add_tax_gl_entries(gl_entries)
+ gl_entries = process_gl_map(gl_entries)
make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
def add_party_gl_entries(self, gl_entries):
@@ -694,10 +699,14 @@
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
for d in self.get("references"):
+ cost_center = self.cost_center
+ if d.reference_doctype == "Sales Invoice" and not cost_center:
+ cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
gle = party_gl_dict.copy()
gle.update({
"against_voucher_type": d.reference_doctype,
- "against_voucher": d.reference_name
+ "against_voucher": d.reference_name,
+ "cost_center": cost_center
})
allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
@@ -732,7 +741,8 @@
"against": self.party if self.payment_type=="Pay" else self.paid_to,
"credit_in_account_currency": self.paid_amount,
"credit": self.base_paid_amount,
- "cost_center": self.cost_center
+ "cost_center": self.cost_center,
+ "post_net_value": True
}, item=self)
)
if self.payment_type in ("Receive", "Internal Transfer"):
@@ -755,19 +765,17 @@
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()
+ payment_account = self.get_party_account_for_taxes()
tax_amount = d.tax_amount
base_tax_amount = d.base_tax_amount
- if self.advance_tax_account:
- tax_amount = -1 * tax_amount
- base_tax_amount = -1 * base_tax_amount
-
gl_entries.append(
self.get_gl_dict({
"account": d.account_head,
@@ -776,20 +784,21 @@
dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency==self.company_currency
else d.tax_amount,
- "cost_center": d.cost_center
+ "cost_center": d.cost_center,
+ "post_net_value": True,
}, 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:
+ if not d.included_in_paid_amount:
gl_entries.append(
self.get_gl_dict({
- "account": payment_or_advance_account,
+ "account": payment_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,
+ "post_net_value": True,
}, account_currency, item=d))
def add_deductions_gl_entries(self, gl_entries):
@@ -811,9 +820,7 @@
)
def get_party_account_for_taxes(self):
- if self.advance_tax_account:
- return self.advance_tax_account
- elif self.payment_type == 'Receive':
+ if self.payment_type == 'Receive':
return self.paid_to
elif self.payment_type in ('Pay', 'Internal Transfer'):
return self.paid_from
@@ -830,7 +837,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:
@@ -887,7 +897,7 @@
self.paid_amount_after_tax = self.paid_amount
def determine_exclusive_rate(self):
- if not any((cint(tax.included_in_paid_amount) for tax in self.get("taxes"))):
+ if not any(cint(tax.included_in_paid_amount) for tax in self.get("taxes")):
return
cumulated_tax_fraction = 0
@@ -1007,7 +1017,7 @@
@frappe.whitelist()
def get_outstanding_reference_documents(args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
if args.get('party_type') == 'Member':
@@ -1026,12 +1036,6 @@
party_account_currency = get_account_currency(args.get("party_account"))
company_currency = frappe.get_cached_value('Company', args.get("company"), "default_currency")
- # Get negative outstanding sales /purchase invoices
- negative_outstanding_invoices = []
- if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
- negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
- args.get("party_account"), args.get("company"), party_account_currency, company_currency)
-
# Get positive outstanding sales /purchase invoices/ Fees
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
@@ -1078,11 +1082,17 @@
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
+ # Get negative outstanding sales /purchase invoices
+ negative_outstanding_invoices = []
+ if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
+ negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
+ args.get("party_account"), party_account_currency, company_currency, condition=condition)
+
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
if not data:
frappe.msgprint(_("No outstanding invoices found for the {0} {1} which qualify the filters you have specified.")
- .format(args.get("party_type").lower(), frappe.bold(args.get("party"))))
+ .format(_(args.get("party_type")).lower(), frappe.bold(args.get("party"))))
return data
@@ -1110,22 +1120,26 @@
'invoice_amount': flt(d.invoice_amount),
'outstanding_amount': flt(d.outstanding_amount),
'payment_amount': payment_term.payment_amount,
- 'payment_term': payment_term.payment_term,
- 'allocated_amount': payment_term.outstanding
+ 'payment_term': payment_term.payment_term
}))
+ outstanding_invoices_after_split = []
if invoice_ref_based_on_payment_terms:
for idx, ref in invoice_ref_based_on_payment_terms.items():
- voucher_no = outstanding_invoices[idx]['voucher_no']
- voucher_type = outstanding_invoices[idx]['voucher_type']
+ voucher_no = ref[0]['voucher_no']
+ voucher_type = ref[0]['voucher_type']
- frappe.msgprint(_("Spliting {} {} into {} rows as per payment terms").format(
+ frappe.msgprint(_("Spliting {} {} into {} row(s) as per Payment Terms").format(
voucher_type, voucher_no, len(ref)), alert=True)
- outstanding_invoices.pop(idx - 1)
- outstanding_invoices += invoice_ref_based_on_payment_terms[idx]
+ outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
- return outstanding_invoices
+ existing_row = list(filter(lambda x: x.get('voucher_no') == voucher_no, outstanding_invoices))
+ index = outstanding_invoices.index(existing_row[0])
+ outstanding_invoices.pop(index)
+
+ outstanding_invoices_after_split += outstanding_invoices
+ return outstanding_invoices_after_split
def get_orders_to_be_billed(posting_date, party_type, party,
company, party_account_currency, company_currency, cost_center=None, filters=None):
@@ -1192,7 +1206,7 @@
return order_list
def get_negative_outstanding_invoices(party_type, party, party_account,
- company, party_account_currency, company_currency, cost_center=None):
+ party_account_currency, company_currency, cost_center=None, condition=None):
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
@@ -1214,19 +1228,21 @@
`tab{voucher_type}`
where
{party_type} = %s and {party_account} = %s and docstatus = 1 and
- company = %s and outstanding_amount < 0
+ outstanding_amount < 0
{supplier_condition}
+ {condition}
order by
posting_date, name
""".format(**{
"supplier_condition": supplier_condition,
+ "condition": condition,
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
"cost_center": cost_center
- }), (party, party_account, company), as_dict=True)
+ }), (party, party_account), as_dict=True)
@frappe.whitelist()
@@ -1386,7 +1402,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
@@ -1406,7 +1422,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":
@@ -1446,7 +1462,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")
@@ -1569,13 +1585,6 @@
})
pe.set_difference_amount()
- if doc.doctype == 'Purchase Order' and doc.apply_tds:
- pe.apply_tax_withholding_amount = 1
- pe.tax_withholding_category = doc.tax_withholding_category
-
- if not pe.advance_tax_account:
- pe.advance_tax_account = frappe.db.get_value('Company', pe.company, 'unrealized_profit_loss_account')
-
return pe
def get_bank_cash_account(doc, bank_account):
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 dac927b..cc3528e 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -1,16 +1,25 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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"]
@@ -324,7 +333,10 @@
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)
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..b71dbb9 100644
--- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
@@ -1,10 +1,9 @@
-# -*- 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.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..fc1cad9 100644
--- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
@@ -1,10 +1,9 @@
-# -*- 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.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 3529c16..25dc4e6 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class PaymentGatewayAccount(Document):
def autoname(self):
self.name = self.payment_gateway + " - " + self.currency
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..3996892 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,8 +1,3 @@
-from __future__ import unicode_literals
-
-from frappe import _
-
-
def get_data():
return {
'fieldname': 'payment_gateway_account',
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..1895c12 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
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 aa373bc..9074def 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.js
+++ b/erpnext/accounts/doctype/payment_order/payment_order.js
@@ -10,6 +10,9 @@
}
}
});
+
+ frm.set_df_property('references', 'cannot_add_rows', true);
+ frm.set_df_property('references', 'cannot_delete_rows', true);
},
refresh: function(frm) {
if (frm.doc.docstatus == 0) {
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py
index 8d29ae7..50a58b8 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order.py
@@ -1,13 +1,14 @@
-# -*- 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.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 a4f3358..37bbaec 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'payment_order',
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 9ba57ae..3f4d89b 100644
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py
@@ -1,15 +1,19 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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()
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..94704fc 100644
--- a/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.py
+++ b/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.py
@@ -1,10 +1,9 @@
-# -*- 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 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..ad5a840 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -2,47 +2,16 @@
// 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;
+ const default_company = frappe.defaults.get_default('company');
+ this.frm.set_value('company', default_company);
- this.frm.set_query("party", function() {
- check_mandatory(me.frm);
- });
+ this.frm.set_value('party_type', '');
+ this.frm.set_value('party', '');
+ this.frm.set_value('receivable_payable_account', '');
- this.frm.set_query("party_type", function() {
+ this.frm.set_query("party_type", () => {
return {
"filters": {
"name": ["in", Object.keys(frappe.boot.party_account_types)],
@@ -50,86 +19,149 @@
}
});
- this.frm.set_query('receivable_payable_account', function() {
- check_mandatory(me.frm);
+ this.frm.set_query('receivable_payable_account', () => {
return {
filters: {
- "company": me.frm.doc.company,
+ "company": this.frm.doc.company,
"is_group": 0,
- "account_type": frappe.boot.party_account_types[me.frm.doc.party_type]
+ "account_type": frappe.boot.party_account_types[this.frm.doc.party_type]
}
};
});
- this.frm.set_query('bank_cash_account', function() {
- check_mandatory(me.frm, true);
+ this.frm.set_query('bank_cash_account', () => {
return {
filters:[
- ['Account', 'company', '=', me.frm.doc.company],
+ ['Account', 'company', '=', this.frm.doc.company],
['Account', 'is_group', '=', 0],
['Account', 'account_type', 'in', ['Bank', 'Cash']]
]
};
});
-
- this.frm.set_value('party_type', '');
- this.frm.set_value('party', '');
- this.frm.set_value('receivable_payable_account', '');
-
- var check_mandatory = (frm, only_company=false) => {
- var title = __("Mandatory");
- if (only_company && !frm.doc.company) {
- frappe.throw({message: __("Please Select a Company First"), title: title});
- } else if (!frm.doc.company || !frm.doc.party_type) {
- frappe.throw({message: __("Please Select Both Company and Party Type First"), title: title});
- }
- };
}
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() {
+ this.frm.set_value('party', '');
+ this.frm.set_value('receivable_payable_account', '');
+ }
+
+ party_type() {
+ this.frm.set_value('party', '');
}
party() {
- var me = this
- if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
+ this.frm.set_value('receivable_payable_account', '');
+ this.frm.trigger("clear_child_tables");
+
+ if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) {
return frappe.call({
method: "erpnext.accounts.party.get_party_account",
args: {
- company: me.frm.doc.company,
- party_type: me.frm.doc.party_type,
- party: me.frm.doc.party
+ company: this.frm.doc.company,
+ party_type: this.frm.doc.party_type,
+ party: this.frm.doc.party
},
- callback: function(r) {
+ callback: (r) => {
if (!r.exc && r.message) {
- me.frm.set_value("receivable_payable_account", r.message);
+ this.frm.set_value("receivable_payable_account", r.message);
}
+ this.frm.refresh();
+
}
});
}
}
+ receivable_payable_account() {
+ this.frm.trigger("clear_child_tables");
+ this.frm.refresh();
+ }
+
+ clear_child_tables() {
+ this.frm.clear_table("invoices");
+ this.frm.clear_table("payments");
+ this.frm.clear_table("allocation");
+ this.frm.refresh_fields();
+ }
+
get_unreconciled_entries() {
- var me = this;
+ this.frm.clear_table("allocation");
return this.frm.call({
- doc: me.frm.doc,
+ doc: this.frm.doc,
method: 'get_unreconciled_entries',
- callback: function(r, rt) {
- me.set_invoice_options();
- me.toggle_primary_action();
+ callback: () => {
+ if (!(this.frm.doc.payments.length || this.frm.doc.invoices.length)) {
+ frappe.throw({message: __("No Unreconciled Invoices and Payments found for this party and account")});
+ } else if (!(this.frm.doc.invoices.length)) {
+ frappe.throw({message: __("No Outstanding Invoices found for this party")});
+ } else if (!(this.frm.doc.payments.length)) {
+ frappe.throw({message: __("No Unreconciled Payments found for this party")});
+ }
+ this.frm.refresh();
}
});
}
+ allocate() {
+ let payments = this.frm.fields_dict.payments.grid.get_selected_children();
+ if (!(payments.length)) {
+ payments = this.frm.doc.payments;
+ }
+ let invoices = this.frm.fields_dict.invoices.grid.get_selected_children();
+ if (!(invoices.length)) {
+ invoices = this.frm.doc.invoices;
+ }
+ return this.frm.call({
+ doc: this.frm.doc,
+ method: 'allocate_entries',
+ args: {
+ payments: payments,
+ invoices: invoices
+ },
+ callback: () => {
+ this.frm.refresh();
+ }
+ });
+ }
+
reconcile() {
- var me = this;
- var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account);
+ var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
if (show_dialog && show_dialog.length) {
@@ -138,7 +170,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;
@@ -161,10 +193,10 @@
label: __("Difference Account"),
fieldname: 'difference_account',
reqd: 1,
- get_query: function() {
+ get_query: () => {
return {
filters: {
- company: me.frm.doc.company,
+ company: this.frm.doc.company,
is_group: 0
}
}
@@ -178,23 +210,23 @@
}]
},
],
- primary_action: function() {
- const args = dialog.get_values()["payments"];
+ primary_action: () => {
+ 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);
});
- me.reconcile_payment_entries();
+ this.reconcile_payment_entries();
dialog.hide();
},
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 +235,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();
@@ -212,54 +244,15 @@
}
reconcile_payment_entries() {
- var me = this;
-
return this.frm.call({
- doc: me.frm.doc,
+ doc: this.frm.doc,
method: 'reconcile',
- callback: function(r, rt) {
- me.set_invoice_options();
- me.toggle_primary_action();
+ callback: () => {
+ this.frm.clear_table("allocation");
+ this.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 acfe1fe..548571d 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -1,15 +1,17 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 +29,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 +66,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 +75,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 +90,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 +101,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 +111,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 +129,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 +153,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 +234,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,25 +340,25 @@
"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)
}
]
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/healthcare/doctype/drug_prescription/__init__.py b/erpnext/accounts/doctype/payment_reconciliation_allocation/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/drug_prescription/__init__.py
rename 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..7665b75 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py
+++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..c0e3fd6 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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 f83cb37..6a84a65 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -1,20 +1,25 @@
-# -*- 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 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):
@@ -542,3 +547,15 @@
}, target_doc, set_missing_values)
return doclist
+
+def validate_payment(doc, method=None):
+ if doc.reference_doctype != "Payment Request" or (
+ frappe.db.get_value(doc.reference_doctype, doc.reference_docname, 'status')
+ != "Paid"
+ ):
+ return
+
+ frappe.throw(
+ _("The Payment Request {0} is already paid, cannot process payment twice")
+ .format(doc.reference_docname)
+ )
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.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 ad6ff6f..f679ccf 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+
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"]
diff --git a/erpnext/accounts/doctype/payment_schedule/payment_schedule.py b/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
index 4174017..33e261f 100644
--- a/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
+++ b/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
@@ -1,8 +1,7 @@
-# -*- 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
diff --git a/erpnext/accounts/doctype/payment_term/payment_term.py b/erpnext/accounts/doctype/payment_term/payment_term.py
index 5d4df05..956c2b1 100644
--- a/erpnext/accounts/doctype/payment_term/payment_term.py
+++ b/erpnext/accounts/doctype/payment_term/payment_term.py
@@ -1,8 +1,7 @@
-# -*- 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
diff --git a/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py b/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py
index d146fcd..ac80b79 100644
--- a/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py
+++ b/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..820610c 100644
--- a/erpnext/accounts/doctype/payment_term/test_payment_term.py
+++ b/erpnext/accounts/doctype/payment_term/test_payment_term.py
@@ -1,7 +1,6 @@
-# -*- coding: utf-8 -*-
# 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.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
index 39627eb..3a6999c 100644
--- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
@@ -1,13 +1,11 @@
-# -*- 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.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 5c8cb4f..2cf7a6c 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
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..8529ef5 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
@@ -1,7 +1,6 @@
-# -*- coding: utf-8 -*-
# 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..710988b 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
@@ -1,8 +1,7 @@
-# -*- 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
diff --git a/erpnext/accounts/doctype/period_closing_voucher/__init__.py b/erpnext/accounts/doctype/period_closing_voucher/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/__init__.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 289278e..d0e555e 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -1,14 +1,18 @@
# 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 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):
@@ -54,7 +58,7 @@
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()
@@ -77,16 +81,17 @@
gl_entries += gle_for_net_pl_bal
return gl_entries
-
+
def get_pnl_gl_entry(self, pl_accounts):
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
gl_entries = []
for acc in pl_accounts:
if flt(acc.bal_in_company_currency):
+ cost_center = acc.cost_center if self.cost_center_wise_pnl else company_cost_center
gl_entry = self.get_gl_dict({
"account": self.closing_account_head,
- "cost_center": acc.cost_center or company_cost_center,
+ "cost_center": cost_center,
"finance_book": acc.finance_book,
"account_currency": acc.account_currency,
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
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 da0eeac..3b6dd87 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -2,70 +2,63 @@
# License: GNU General Public License v3. See license.txt
-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 erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+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'")
@@ -73,8 +66,8 @@
company = create_company()
surplus_account = create_account()
- cost_center1 = create_cost_center("Test Cost Center 1")
- cost_center2 = create_cost_center("Test Cost Center 2")
+ cost_center1 = create_cost_center("Main")
+ cost_center2 = create_cost_center("Western Branch")
create_sales_invoice(
company=company,
@@ -95,31 +88,26 @@
currency="USD"
)
- 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 = self.make_period_closing_voucher(submit=False)
+ pcv.cost_center_wise_pnl = 1
+ pcv.save()
pcv.submit()
+ 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'")
@@ -148,43 +136,40 @@
jv.save()
jv.submit()
- pcv = frappe.get_doc({
- "transaction_date": today(),
- "posting_date": today(),
- "fiscal_year": get_fiscal_year(today())[0],
- "company": company,
- "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 = (
- (surplus_account, 0.0, 400.0, ''),
+ (surplus_account, 0.0, 400.0, None),
(surplus_account, 0.0, 400.0, jv.finance_book),
- ('Sales - TPC', 400.0, 0.0, ''),
+ ('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
+ select account, debit, credit, finance_book
+ from `tabGL Entry` where voucher_no=%s
+ order by account, finance_book
""", (pcv.name))
- self.assertTrue(pcv_gle, expected_gle)
+ self.assertEqual(pcv_gle, expected_gle)
- def make_period_closing_voucher(self):
+ def make_period_closing_voucher(self, submit=True):
+ surplus_account = create_account()
+ cost_center = create_cost_center("Test Cost Center 1")
pcv = frappe.get_doc({
"doctype": "Period Closing Voucher",
- "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()
- pcv.submit()
+ if submit:
+ pcv.submit()
return pcv
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
index 4d6e4a2..d6e35c6 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
@@ -180,8 +180,7 @@
"fieldname": "pos_transactions",
"fieldtype": "Table",
"label": "POS Transactions",
- "options": "POS Invoice Reference",
- "reqd": 1
+ "options": "POS Invoice Reference"
},
{
"fieldname": "pos_opening_entry",
@@ -229,7 +228,7 @@
"link_fieldname": "pos_closing_entry"
}
],
- "modified": "2021-05-05 16:59:49.723261",
+ "modified": "2021-10-20 16:19:25.340565",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Closing Entry",
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 8252872..07059cb 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -1,15 +1,17 @@
-# -*- 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
-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 5b18ebb..626bee0 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
@@ -1,15 +1,18 @@
-# -*- 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 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):
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..2e34e44 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
@@ -1,9 +1,9 @@
-# -*- 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 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..cfedeb3 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
@@ -1,9 +1,9 @@
-# -*- 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 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..5e532ae 100644
--- a/erpnext/accounts/doctype/pos_customer_group/pos_customer_group.py
+++ b/erpnext/accounts/doctype/pos_customer_group/pos_customer_group.py
@@ -1,10 +1,9 @@
-# -*- 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.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..4ef8827 100644
--- a/erpnext/accounts/doctype/pos_field/pos_field.py
+++ b/erpnext/accounts/doctype/pos_field/pos_field.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 POSField(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 19c6c8f..0c6e7ed 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -171,6 +171,7 @@
"sales_team_section_break",
"sales_partner",
"column_break10",
+ "amount_eligible_for_commission",
"commission_rate",
"total_commission",
"section_break2",
@@ -622,6 +623,7 @@
"read_only": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"label": "Packing List",
@@ -629,6 +631,7 @@
"print_hide": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packed_items",
"fieldtype": "Table",
"label": "Packed Items",
@@ -1559,16 +1562,23 @@
"label": "Coupon Code",
"options": "Coupon Code",
"print_hide": 1
+ },
+ {
+ "fieldname": "amount_eligible_for_commission",
+ "fieldtype": "Currency",
+ "label": "Amount Eligible for Commission",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
- "modified": "2021-08-24 18:19:20.728433",
+ "modified": "2021-10-05 12:11:53.871828",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
"name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 034a217..0d6404c 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -1,20 +1,22 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
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):
@@ -35,6 +37,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()
@@ -309,7 +312,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:
@@ -360,7 +363,7 @@
for item in self.get("items"):
if item.get('item_code'):
profile_details = get_pos_profile_item_details(profile.get("company"), frappe._dict(item.as_dict()), profile)
- for fname, val in iteritems(profile_details):
+ for fname, val in profile_details.items():
if (not for_validate) or (for_validate and not item.get(fname)):
item.set(fname, val)
@@ -519,9 +522,9 @@
@frappe.whitelist()
def make_merge_log(invoices):
import json
- from six import string_types
- if isinstance(invoices, string_types):
+
+ if isinstance(invoices, str):
invoices = json.loads(invoices)
if len(invoices) == 0:
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index d2527fb..6696333 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -1,16 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# 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 +215,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 +241,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 +295,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')
@@ -324,8 +326,8 @@
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')
@@ -353,8 +355,10 @@
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")
@@ -374,7 +378,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)
@@ -392,8 +399,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()
@@ -416,8 +427,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()
@@ -457,8 +472,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.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
index 8b71eb0..3f85668 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
@@ -46,6 +46,7 @@
"base_amount",
"pricing_rules",
"is_free_item",
+ "grant_commission",
"section_break_21",
"net_rate",
"net_amount",
@@ -800,14 +801,22 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "grant_commission",
+ "fieldtype": "Check",
+ "label": "Grant Commission",
+ "read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2021-01-04 17:34:49.924531",
+ "modified": "2021-10-05 12:23:47.506290",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Item",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
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..7e3ae82 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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 2f8081b..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,7 +4,7 @@
frappe.ui.form.on('POS Invoice Merge Log', {
setup: function(frm) {
frm.set_query("pos_invoice", "pos_invoices", doc => {
- return{
+ return {
filters: {
'docstatus': 1,
'customer': doc.customer,
@@ -12,5 +12,10 @@
}
}
});
+ },
+
+ 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 e50d437..0720d9b 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
@@ -1,19 +1,18 @@
-# -*- 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 json
+
import frappe
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 +20,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))
@@ -108,7 +110,15 @@
def merge_pos_invoice_into(self, invoice, data):
items, payments, taxes = [], [], []
+
loyalty_amount_sum, loyalty_points_sum = 0, 0
+
+ rounding_adjustment, base_rounding_adjustment = 0, 0
+ rounded_total, base_rounded_total = 0, 0
+
+ loyalty_amount_sum, loyalty_points_sum, idx = 0, 0, 1
+
+
for doc in data:
map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
@@ -122,7 +132,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
@@ -142,6 +152,8 @@
found = True
if not found:
tax.charge_type = 'Actual'
+ tax.idx = idx
+ idx += 1
tax.included_in_print_rate = 0
tax.tax_amount = tax.tax_amount_after_discount_amount
tax.base_tax_amount = tax.base_tax_amount_after_discount_amount
@@ -157,6 +169,11 @@
found = True
if not found:
payments.append(payment)
+ rounding_adjustment += doc.rounding_adjustment
+ rounded_total += doc.rounded_total
+ base_rounding_adjustment += doc.base_rounding_adjustment
+ base_rounded_total += doc.base_rounded_total
+
if loyalty_points_sum:
invoice.redeem_loyalty_points = 1
@@ -166,10 +183,19 @@
invoice.set('items', items)
invoice.set('payments', payments)
invoice.set('taxes', taxes)
+ invoice.set('rounding_adjustment',rounding_adjustment)
+ invoice.set('base_rounding_adjustment',base_rounding_adjustment)
+ invoice.set('rounded_total',rounded_total)
+ invoice.set('base_rounded_total',base_rounded_total)
invoice.additional_discount_percentage = 0
invoice.discount_amount = 0.0
invoice.taxes_and_charges = None
invoice.ignore_pricing_rule = 1
+ invoice.customer = self.customer
+
+ if self.merge_invoices_based_on == 'Customer Group':
+ invoice.flags.ignore_pos_profile = True
+ invoice.pos_profile = ''
return invoice
@@ -226,7 +252,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')
@@ -236,7 +262,10 @@
return pos_invoice_customer_map
def consolidate_pos_invoices(pos_invoices=None, closing_entry=None):
- invoices = pos_invoices or (closing_entry and closing_entry.get('pos_transactions')) or get_all_unconsolidated_invoices()
+ invoices = pos_invoices or (closing_entry and closing_entry.get('pos_transactions'))
+ if frappe.flags.in_test and not invoices:
+ invoices = get_all_unconsolidated_invoices()
+
invoice_by_customer = get_invoice_customer_map(invoices)
if len(invoices) >= 10 and closing_entry:
@@ -260,7 +289,7 @@
def create_merge_logs(invoice_by_customer, closing_entry=None):
try:
- for customer, invoices in six.iteritems(invoice_by_customer):
+ for customer, invoices in invoice_by_customer.items():
merge_log = frappe.new_doc('POS Invoice Merge Log')
merge_log.posting_date = getdate(closing_entry.get('posting_date')) if closing_entry else nowdate()
merge_log.customer = customer
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 1b96594..3555da8 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
@@ -1,15 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import json
+import unittest
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
+
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):
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..c1c36f8 100644
--- a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.py
+++ b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..75fa080 100644
--- a/erpnext/accounts/doctype/pos_item_group/pos_item_group.py
+++ b/erpnext/accounts/doctype/pos_item_group/pos_item_group.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class POSItemGroup(Document):
pass
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 3318fef..0b2e045 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
from frappe 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()
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 c115be5..105d53d 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestPOSOpeningEntry(unittest.TestCase):
pass
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..9108b90 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..589047d 100644
--- a/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.py
+++ b/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class POSPaymentMethod(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js
index efdeb1a..813d20d 100755
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.js
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js
@@ -3,22 +3,20 @@
{% include "erpnext/public/js/controllers/accounts.js" %}
-frappe.ui.form.on("POS Profile", "onload", function(frm) {
- frm.set_query("selling_price_list", function() {
- return { filters: { selling: 1 } };
- });
-
- frm.set_query("tc_name", function() {
- return { filters: { selling: 1 } };
- });
-
- erpnext.queries.setup_queries(frm, "Warehouse", function() {
- return erpnext.queries.warehouse(frm.doc);
- });
-});
-
frappe.ui.form.on('POS Profile', {
setup: function(frm) {
+ frm.set_query("selling_price_list", function() {
+ return { filters: { selling: 1 } };
+ });
+
+ frm.set_query("tc_name", function() {
+ return { filters: { selling: 1 } };
+ });
+
+ erpnext.queries.setup_queries(frm, "Warehouse", function() {
+ return erpnext.queries.warehouse(frm.doc);
+ });
+
frm.set_query("print_format", function() {
return {
filters: [
@@ -27,10 +25,16 @@
};
});
- frm.set_query("account_for_change_amount", function() {
+ frm.set_query("account_for_change_amount", function(doc) {
+ if (!doc.company) {
+ frappe.throw(__('Please set Company'));
+ }
+
return {
filters: {
- account_type: ['in', ["Cash", "Bank"]]
+ account_type: ['in', ["Cash", "Bank"]],
+ is_group: 0,
+ company: doc.company
}
};
});
@@ -45,7 +49,7 @@
});
frm.set_query('company_address', function(doc) {
- if(!doc.company) {
+ if (!doc.company) {
frappe.throw(__('Please set Company'));
}
@@ -58,11 +62,79 @@
};
});
+ frm.set_query('income_account', function(doc) {
+ if (!doc.company) {
+ frappe.throw(__('Please set Company'));
+ }
+
+ return {
+ filters: {
+ 'is_group': 0,
+ 'company': doc.company,
+ 'account_type': "Income Account"
+ }
+ };
+ });
+
+ frm.set_query('cost_center', function(doc) {
+ if (!doc.company) {
+ frappe.throw(__('Please set Company'));
+ }
+
+ return {
+ filters: {
+ 'company': doc.company,
+ 'is_group': 0
+ }
+ };
+ });
+
+ frm.set_query('expense_account', function(doc) {
+ if (!doc.company) {
+ frappe.throw(__('Please set Company'));
+ }
+
+ return {
+ filters: {
+ "report_type": "Profit and Loss",
+ "company": doc.company,
+ "is_group": 0
+ }
+ };
+ });
+
+ frm.set_query("select_print_heading", function() {
+ return {
+ filters: [
+ ['Print Heading', 'docstatus', '!=', 2]
+ ]
+ };
+ });
+
+ frm.set_query("write_off_account", function(doc) {
+ return {
+ filters: {
+ 'report_type': 'Profit and Loss',
+ 'is_group': 0,
+ 'company': doc.company
+ }
+ };
+ });
+
+ frm.set_query("write_off_cost_center", function(doc) {
+ return {
+ filters: {
+ 'is_group': 0,
+ 'company': doc.company
+ }
+ };
+ });
+
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
},
refresh: function(frm) {
- if(frm.doc.company) {
+ if (frm.doc.company) {
frm.trigger("toggle_display_account_head");
}
},
@@ -76,71 +148,4 @@
frm.toggle_display('expense_account',
erpnext.is_perpetual_inventory_enabled(frm.doc.company));
}
-})
-
-// Income Account
-// --------------------------------
-cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) {
- return{
- filters:{
- 'is_group': 0,
- 'company': doc.company,
- 'account_type': "Income Account"
- }
- };
-};
-
-
-// Cost Center
-// -----------------------------
-cur_frm.fields_dict['cost_center'].get_query = function(doc,cdt,cdn) {
- return{
- filters:{
- 'company': doc.company,
- 'is_group': 0
- }
- };
-};
-
-
-// Expense Account
-// -----------------------------
-cur_frm.fields_dict["expense_account"].get_query = function(doc) {
- return {
- filters: {
- "report_type": "Profit and Loss",
- "company": doc.company,
- "is_group": 0
- }
- };
-};
-
-// ------------------ Get Print Heading ------------------------------------
-cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) {
- return{
- filters:[
- ['Print Heading', 'docstatus', '!=', 2]
- ]
- };
-};
-
-cur_frm.fields_dict.write_off_account.get_query = function(doc) {
- return{
- filters:{
- 'report_type': 'Profit and Loss',
- 'is_group': 0,
- 'company': doc.company
- }
- };
-};
-
-// Write off cost center
-// -----------------------
-cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) {
- return{
- filters:{
- 'is_group': 0,
- 'company': doc.company
- }
- };
-};
+});
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 8afa0ab..9c9f37b 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -120,6 +120,7 @@
{
"fieldname": "payments",
"fieldtype": "Table",
+ "label": "Payment Methods",
"options": "POS Payment Method",
"reqd": 1
},
@@ -377,7 +378,7 @@
"link_fieldname": "pos_profile"
}
],
- "modified": "2021-02-01 13:52:51.081311",
+ "modified": "2021-10-14 14:17:00.469298",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index cf7ed26..1d49c3d 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -1,12 +1,12 @@
# 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 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
+
class POSProfile(Document):
def validate(self):
@@ -36,7 +36,7 @@
self.expense_account], "Cost Center": [self.cost_center],
"Warehouse": [self.warehouse]}
- for link_dt, dn_list in iteritems(accounts):
+ for link_dt, dn_list in accounts.items():
for link_dn in dn_list:
if link_dn and not frappe.db.exists({"doctype": link_dt,
"company": self.company, "name": link_dn}):
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..c8cf0d2 100644
--- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from erpnext.stock.get_item_details import get_pos_profile
+
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 +31,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..c92d563 100644
--- a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.py
+++ b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.py
@@ -1,9 +1,9 @@
-# -*- 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 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..3aafb1d 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..fd55dc8 100644
--- a/erpnext/accounts/doctype/pos_search_fields/pos_search_fields.py
+++ b/erpnext/accounts/doctype/pos_search_fields/pos_search_fields.py
@@ -1,10 +1,10 @@
-# -*- 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 POSSearchFields(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js
index 9003af5..7d8f356 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.js
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js
@@ -2,11 +2,11 @@
// For license information, please see license.txt
let search_fields_datatypes = ['Data', 'Link', 'Dynamic Link', 'Long Text', 'Select', 'Small Text', 'Text', 'Text Editor'];
-let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "hub_sync_id", "asset_naming_series",
+let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "asset_naming_series",
"default_material_request_type", "valuation_method", "warranty_period", "weight_uom", "batch_number_series",
"serial_no_series", "purchase_uom", "customs_tariff_number", "sales_uom", "deferred_revenue_account",
"deferred_expense_account", "quality_inspection_template", "route", "slideshow", "website_image_alt", "thumbnail",
- "web_long_description", "hub_sync_id"]
+ "web_long_description"]
frappe.ui.form.on('POS Settings', {
onload: function(frm) {
diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.py b/erpnext/accounts/doctype/pos_settings/pos_settings.py
index d925dd9..2adecc0 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.py
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.py
@@ -1,11 +1,10 @@
-# -*- 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.model.document import Document
+
class POSSettings(Document):
def validate(self):
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..630b305 100644
--- a/erpnext/accounts/doctype/pos_settings/test_pos_settings.py
+++ b/erpnext/accounts/doctype/pos_settings/test_pos_settings.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestPOSSettings(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
index d79ad5f..8267582 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
@@ -38,7 +38,7 @@
refresh: function(frm) {
var help_content =
- `<table class="table table-bordered" style="background-color: #f9f9f9;">
+ `<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
<tr><td>
<h4>
<i class="fa fa-hand-right"></i>
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 4903c50..ac96b04 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -2,17 +2,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 six import string_types
+from frappe.utils import cint, flt, getdate
apply_on_dict = {"Item Code": "items",
"Item Group": "item_groups", "Brand": "brands"}
@@ -179,7 +177,7 @@
}
"""
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
args = frappe._dict(args)
@@ -203,13 +201,13 @@
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 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))
@@ -228,10 +226,14 @@
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):
+ if isinstance(doc, str):
doc = json.loads(doc)
if doc:
@@ -267,7 +269,7 @@
for pricing_rule in pricing_rules:
if not pricing_rule: continue
- if isinstance(pricing_rule, string_types):
+ if isinstance(pricing_rule, str):
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule)
@@ -315,9 +317,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))
@@ -390,8 +391,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)
@@ -423,7 +426,7 @@
@frappe.whitelist()
def remove_pricing_rules(item_list):
- if isinstance(item_list, string_types):
+ if isinstance(item_list, str):
item_list = json.loads(item_list)
out = []
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 680370b..d8b8606 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -2,15 +2,16 @@
# License: GNU General Public License v3. See license.txt
-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 +22,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 +105,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 +199,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",
@@ -539,6 +543,75 @@
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
item.delete()
+ def test_pricing_rule_for_different_currency(self):
+ make_item("Test Sanitizer Item")
+
+ pricing_rule_record = {
+ "doctype": "Pricing Rule",
+ "title": "_Test Sanitizer Rule",
+ "apply_on": "Item Code",
+ "items": [{
+ "item_code": "Test Sanitizer Item",
+ }],
+ "selling": 1,
+ "currency": "INR",
+ "rate_or_discount": "Rate",
+ "rate": 0,
+ "priority": 2,
+ "margin_type": "Percentage",
+ "margin_rate_or_amount": 0.0,
+ "company": "_Test Company"
+ }
+
+ rule = frappe.get_doc(pricing_rule_record)
+ rule.rate_or_discount = 'Rate'
+ rule.rate = 100.0
+ rule.insert()
+
+ rule1 = frappe.get_doc(pricing_rule_record)
+ rule1.currency = 'USD'
+ rule1.rate_or_discount = 'Rate'
+ rule1.rate = 2.0
+ rule1.priority = 1
+ rule1.insert()
+
+ args = frappe._dict({
+ "item_code": "Test Sanitizer Item",
+ "company": "_Test Company",
+ "price_list": "_Test Price List",
+ "currency": "USD",
+ "doctype": "Sales Invoice",
+ "conversion_rate": 1,
+ "price_list_currency": "_Test Currency",
+ "plc_conversion_rate": 1,
+ "order_type": "Sales",
+ "customer": "_Test Customer",
+ "name": None,
+ "transaction_date": frappe.utils.nowdate()
+ })
+
+ details = get_item_details(args)
+ self.assertEqual(details.price_list_rate, 2.0)
+
+
+ args = frappe._dict({
+ "item_code": "Test Sanitizer Item",
+ "company": "_Test Company",
+ "price_list": "_Test Price List",
+ "currency": "INR",
+ "doctype": "Sales Invoice",
+ "conversion_rate": 1,
+ "price_list_currency": "_Test Currency",
+ "plc_conversion_rate": 1,
+ "order_type": "Sales",
+ "customer": "_Test Customer",
+ "name": None,
+ "transaction_date": frappe.utils.nowdate()
+ })
+
+ details = get_item_details(args)
+ self.assertEqual(details.price_list_rate, 100.0)
+
def test_pricing_rule_for_transaction(self):
make_item("Water Flask 1")
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
@@ -616,3 +689,12 @@
"Pricing Rule Item Group", "Pricing Rule Brand"]:
frappe.db.sql("delete from `tab{0}`".format(doctype))
+
+
+def make_item_price(item, price_list_name, item_price):
+ frappe.get_doc({
+ 'doctype': 'Item Price',
+ 'price_list': price_list_name,
+ 'item_code': item,
+ 'price_list_rate': item_price
+ }).insert(ignore_permissions=True, ignore_mandatory=True)
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 94abf3b..02bfc9d 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -3,19 +3,18 @@
# For license information, please see license.txt
-from __future__ import unicode_literals
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
@@ -29,6 +28,9 @@
pricing_rules = []
values = {}
+ if not frappe.db.exists('Pricing Rule', {'disable': 0, args.transaction_type: 1}):
+ return
+
for apply_on in ['Item Code', 'Item Group', 'Brand']:
pricing_rules.extend(_get_pricing_rules(apply_on, args, values))
if pricing_rules and not apply_multiple_pricing_rules(pricing_rules):
@@ -81,7 +83,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)
@@ -262,6 +264,11 @@
else:
p.variant_of = None
+ if len(pricing_rules) > 1:
+ filtered_rules = list(filter(lambda x: x.currency==args.get('currency'), pricing_rules))
+ if filtered_rules:
+ pricing_rules = filtered_rules
+
# find pricing rule with highest priority
if pricing_rules:
max_priority = max(cint(p.priority) for p in pricing_rules)
@@ -398,7 +405,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 +484,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..fdd5be0 100644
--- a/erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.py
+++ b/erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..f48b569 100644
--- a/erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.py
+++ b/erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..f8a2c0e 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..940350a 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 5e7583a..d544f97 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
+++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
@@ -1,15 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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):
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 03c269a..757d0fa 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
@@ -1,13 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
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):
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 a12ea40..09aa723 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
@@ -1,24 +1,24 @@
-# -*- 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 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 +158,7 @@
if doc.cc_to != '':
try:
cc=[frappe.get_value('User', doc.cc_to, 'email')]
- except:
+ except Exception:
pass
return recipients, cc
@@ -194,7 +194,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 +209,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:
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..c281040 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
@@ -1,10 +1,9 @@
-# -*- 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 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..94fcb78 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProcessStatementOfAccountsCustomer(Document):
pass
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
index 3d7a891..5fbe93e 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
@@ -1,12 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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
-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',
@@ -23,6 +20,9 @@
product_discount_fields = ['free_item', 'free_qty', 'free_item_uom',
'free_item_rate', 'same_item', 'is_recursive', 'apply_multiple_pricing_rules']
+class TransactionExists(frappe.ValidationError):
+ pass
+
class PromotionalScheme(Document):
def validate(self):
if not self.selling and not self.buying:
@@ -31,6 +31,40 @@
or self.product_discount_slabs):
frappe.throw(_("Price or product discount slabs are required"))
+ self.validate_applicable_for()
+ self.validate_pricing_rules()
+
+ def validate_applicable_for(self):
+ if self.applicable_for:
+ applicable_for = frappe.scrub(self.applicable_for)
+
+ if not self.get(applicable_for):
+ msg = (f'The field {frappe.bold(self.applicable_for)} is required')
+ frappe.throw(_(msg))
+
+ def validate_pricing_rules(self):
+ if self.is_new():
+ return
+
+ transaction_exists = False
+ docnames = []
+
+ # If user has changed applicable for
+ if self._doc_before_save.applicable_for == self.applicable_for:
+ return
+
+ docnames = frappe.get_all('Pricing Rule',
+ filters= {'promotional_scheme': self.name})
+
+ for docname in docnames:
+ if frappe.db.exists('Pricing Rule Detail',
+ {'pricing_rule': docname.name, 'docstatus': ('<', 2)}):
+ raise_for_transaction_exists(self.name)
+
+ if docnames and not transaction_exists:
+ for docname in docnames:
+ frappe.delete_doc('Pricing Rule', docname.name)
+
def on_update(self):
pricing_rules = frappe.get_all(
'Pricing Rule',
@@ -70,7 +104,16 @@
{'promotional_scheme': self.name}):
frappe.delete_doc('Pricing Rule', rule.name)
-def get_pricing_rules(doc, rules = {}):
+def raise_for_transaction_exists(name):
+ msg = (f"""You can't change the {frappe.bold(_('Applicable For'))}
+ because transactions are present against the Promotional Scheme {frappe.bold(name)}. """)
+ msg += 'Kindly disable this Promotional Scheme and create new for new Applicable For.'
+
+ frappe.throw(_(msg), TransactionExists)
+
+def get_pricing_rules(doc, rules=None):
+ if rules is None:
+ rules = {}
new_doc = []
for child_doc, fields in {'price_discount_slabs': price_discount_fields,
'product_discount_slabs': product_discount_fields}.items():
@@ -79,49 +122,65 @@
return new_doc
-def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}):
+def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
+ if rules is None:
+ rules = {}
new_doc = []
args = get_args_for_pricing_rule(doc)
applicable_for = frappe.scrub(doc.get('applicable_for'))
+
for idx, d in enumerate(doc.get(child_doc)):
if d.name in rules:
- 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)
-
+ if not args.get(applicable_for):
+ docname = get_pricing_rule_docname(d)
+ pr = prepare_pricing_rule(args, doc, child_doc, discount_fields, d, docname)
new_doc.append(pr)
+ else:
+ for applicable_for_value in args.get(applicable_for):
+ docname = get_pricing_rule_docname(d, applicable_for, applicable_for_value)
+ pr = prepare_pricing_rule(args, doc, child_doc, discount_fields,
+ d, docname, applicable_for, applicable_for_value)
+ new_doc.append(pr)
- else:
+ elif args.get(applicable_for):
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)
+ pr = prepare_pricing_rule(args, doc, child_doc, discount_fields,
+ d, applicable_for=applicable_for, value= applicable_for_value)
+
new_doc.append(pr)
+ else:
+ pr = prepare_pricing_rule(args, doc, child_doc, discount_fields, d)
+ new_doc.append(pr)
return new_doc
+def get_pricing_rule_docname(row: dict, applicable_for: str = None, applicable_for_value: str = None) -> str:
+ fields = ['promotional_scheme_id', 'name']
+ filters = {
+ 'promotional_scheme_id': row.name
+ }
+ if applicable_for:
+ fields.append(applicable_for)
+ filters[applicable_for] = applicable_for_value
+ docname = frappe.get_all('Pricing Rule', fields = fields, filters = filters)
+ return docname[0].name if docname else ''
+
+def prepare_pricing_rule(args, doc, child_doc, discount_fields, d, docname=None, applicable_for=None, value=None):
+ if docname:
+ pr = frappe.get_doc("Pricing Rule", docname)
+ else:
+ pr = frappe.new_doc("Pricing Rule")
+
+ pr.title = doc.name
+ temp_args = args.copy()
+
+ if value:
+ temp_args[applicable_for] = value
+
+ return set_args(temp_args, pr, doc, child_doc, discount_fields, d)
def set_args(args, pr, doc, child_doc, discount_fields, child_doc_fields):
pr.update(args)
@@ -144,6 +203,7 @@
apply_on: d.get(apply_on),
'uom': d.uom
})
+
return pr
def get_args_for_pricing_rule(doc):
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py
index 54fedb7..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',
diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
index 286f7cf..49192a4 100644
--- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
@@ -1,14 +1,21 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+from erpnext.accounts.doctype.promotional_scheme.promotional_scheme import TransactionExists
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+
+
class TestPromotionalScheme(unittest.TestCase):
+ def setUp(self):
+ if frappe.db.exists('Promotional Scheme', '_Test Scheme'):
+ frappe.delete_doc('Promotional Scheme', '_Test Scheme')
+
def test_promotional_scheme(self):
- ps = make_promotional_scheme()
+ ps = make_promotional_scheme(applicable_for='Customer', customer='_Test Customer')
price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"],
filters = {'promotional_scheme': ps.name})
self.assertTrue(len(price_rules),1)
@@ -39,22 +46,62 @@
filters = {'promotional_scheme': ps.name})
self.assertEqual(price_rules, [])
-def make_promotional_scheme():
+ def test_promotional_scheme_without_applicable_for(self):
+ ps = make_promotional_scheme()
+ price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+
+ self.assertTrue(len(price_rules), 1)
+ frappe.delete_doc('Promotional Scheme', ps.name)
+
+ price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+ self.assertEqual(price_rules, [])
+
+ def test_change_applicable_for_in_promotional_scheme(self):
+ ps = make_promotional_scheme()
+ price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+ self.assertTrue(len(price_rules), 1)
+
+ so = make_sales_order(qty=5, currency='USD', do_not_save=True)
+ so.set_missing_values()
+ so.save()
+ self.assertEqual(price_rules[0].name, so.pricing_rules[0].pricing_rule)
+
+ ps.applicable_for = 'Customer'
+ ps.append('customer', {
+ 'customer': '_Test Customer'
+ })
+
+ self.assertRaises(TransactionExists, ps.save)
+
+ frappe.delete_doc('Sales Order', so.name)
+ frappe.delete_doc('Promotional Scheme', ps.name)
+ price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+ self.assertEqual(price_rules, [])
+
+def make_promotional_scheme(**args):
+ args = frappe._dict(args)
+
ps = frappe.new_doc('Promotional Scheme')
ps.name = '_Test Scheme'
ps.append('items',{
'item_code': '_Test Item'
})
+
ps.selling = 1
ps.append('price_discount_slabs',{
'min_qty': 4,
+ 'validate_applied_rule': 0,
'discount_percentage': 20,
'rule_description': 'Test'
})
- ps.applicable_for = 'Customer'
- ps.append('customer',{
- 'customer': "_Test Customer"
- })
+
+ ps.company = '_Test Company'
+ if args.applicable_for:
+ ps.applicable_for = args.applicable_for
+ ps.append(frappe.scrub(args.applicable_for), {
+ frappe.scrub(args.applicable_for): args.get(frappe.scrub(args.applicable_for))
+ })
+
ps.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 a70d5c9..aa3696d 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
@@ -136,7 +136,7 @@
"label": "Threshold for Suggestion"
},
{
- "default": "1",
+ "default": "0",
"fieldname": "validate_applied_rule",
"fieldtype": "Check",
"label": "Validate Applied Rule"
@@ -169,7 +169,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-08-19 15:49:29.598727",
+ "modified": "2021-11-16 00:25:33.843996",
"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..a0e2200 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..c9f6f86 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..877998a 100644
--- a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py
+++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..de22bb7 100644
--- a/erpnext/accounts/doctype/psoa_project/psoa_project.py
+++ b/erpnext/accounts/doctype/psoa_project/psoa_project.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class PSOAProject(Document):
pass
diff --git a/erpnext/accounts/doctype/purchase_invoice/__init__.py b/erpnext/accounts/doctype/purchase_invoice/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/purchase_invoice/__init__.py
+++ b/erpnext/accounts/doctype/purchase_invoice/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 6c74d2b..1a398ab 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -590,5 +590,20 @@
company: function(frm) {
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+
+ if (frm.doc.company) {
+ frappe.call({
+ method:
+ "erpnext.accounts.party.get_party_account",
+ args: {
+ party_type: 'Supplier',
+ party: frm.doc.supplier,
+ company: frm.doc.company
+ },
+ callback: (response) => {
+ if (response) frm.set_value("credit_to", response.message);
+ },
+ });
+ }
},
})
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 7822f74..bd01164 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -130,6 +130,7 @@
"allocate_advances_automatically",
"get_advances",
"advances",
+ "advance_tax",
"payment_schedule_section",
"payment_terms_template",
"ignore_default_payment_terms_template",
@@ -149,16 +150,18 @@
"cb_17",
"hold_comment",
"more_info",
+ "status",
+ "inter_company_invoice_reference",
+ "represents_company",
+ "column_break_147",
+ "is_internal_supplier",
+ "accounting_details_section",
"credit_to",
"party_account_currency",
"is_opening",
"against_expense_account",
"column_break_63",
"unrealized_profit_loss_account",
- "status",
- "inter_company_invoice_reference",
- "is_internal_supplier",
- "represents_company",
"remarks",
"subscription_section",
"from_date",
@@ -177,9 +180,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 +192,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 +204,7 @@
"options": "Supplier",
"print_hide": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"bold": 1,
@@ -219,9 +216,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 +224,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 +246,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 +268,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 +286,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 +295,6 @@
"no_copy": 1,
"print_hide": 1,
"print_width": "100px",
- "show_days": 1,
- "show_seconds": 1,
"width": "100px"
},
{
@@ -328,9 +303,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 +315,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 +360,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 +373,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 +389,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 +416,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 +469,7 @@
"oldfieldname": "currency",
"oldfieldtype": "Select",
"options": "Currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "conversion_rate",
@@ -553,24 +478,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 +497,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 +513,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 +526,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 +536,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,26 +548,20 @@
"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",
@@ -678,33 +577,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",
@@ -712,23 +603,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",
@@ -736,9 +621,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",
@@ -748,24 +631,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",
@@ -775,56 +652,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",
@@ -833,9 +696,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",
@@ -843,17 +704,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",
@@ -862,17 +719,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",
@@ -882,9 +735,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",
@@ -894,9 +745,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",
@@ -906,15 +755,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",
@@ -924,9 +769,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",
@@ -936,9 +779,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",
@@ -946,18 +787,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",
@@ -965,9 +802,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",
@@ -975,38 +810,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",
@@ -1016,9 +841,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",
@@ -1028,9 +851,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",
@@ -1040,9 +861,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",
@@ -1052,17 +871,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%"
},
{
@@ -1073,9 +888,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",
@@ -1085,9 +898,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",
@@ -1097,9 +908,7 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "in_words",
@@ -1109,9 +918,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",
@@ -1122,9 +929,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",
@@ -1135,18 +940,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,
@@ -1154,26 +955,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",
@@ -1181,15 +976,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",
@@ -1198,9 +989,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",
@@ -1209,9 +998,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,
@@ -1219,9 +1006,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",
@@ -1229,9 +1014,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",
@@ -1240,15 +1023,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",
@@ -1256,9 +1035,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",
@@ -1266,9 +1043,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,
@@ -1278,17 +1053,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",
@@ -1296,9 +1067,7 @@
"fieldtype": "Button",
"label": "Get Advances Paid",
"oldfieldtype": "Button",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "advances",
@@ -1308,26 +1077,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",
@@ -1335,9 +1098,7 @@
"label": "Payment Schedule",
"no_copy": 1,
"options": "Payment Schedule",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1345,33 +1106,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,
@@ -1379,9 +1132,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,
@@ -1389,15 +1140,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,
@@ -1409,18 +1156,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,
@@ -1429,9 +1172,16 @@
"label": "More Information",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
+ },
+ {
+ "default": "0",
+ "fetch_from": "supplier.is_internal_supplier",
+ "fieldname": "is_internal_supplier",
+ "fieldtype": "Check",
+ "ignore_user_permissions": 1,
+ "label": "Is Internal Supplier",
+ "read_only": 1
},
{
"fieldname": "credit_to",
@@ -1442,9 +1192,7 @@
"options": "Account",
"print_hide": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"fieldname": "party_account_currency",
@@ -1454,21 +1202,17 @@
"no_copy": 1,
"options": "Currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "No",
"fieldname": "is_opening",
"fieldtype": "Select",
- "label": "Is Opening",
+ "label": "Is Opening Entry",
"oldfieldname": "is_opening",
"oldfieldtype": "Select",
"options": "No\nYes",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "against_expense_account",
@@ -1478,15 +1222,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",
@@ -1494,10 +1234,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",
@@ -1506,9 +1244,7 @@
"no_copy": 1,
"options": "Sales Invoice",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "remarks",
@@ -1517,18 +1253,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,
@@ -1537,9 +1269,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,
@@ -1548,15 +1278,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",
@@ -1565,42 +1291,24 @@
"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
- },
- {
- "default": "0",
- "fetch_from": "supplier.is_internal_supplier",
- "fieldname": "is_internal_supplier",
- "fieldtype": "Check",
- "label": "Is Internal Supplier",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "tax_withholding_category",
@@ -1608,33 +1316,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",
@@ -1642,9 +1342,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",
@@ -1653,9 +1351,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",
@@ -1667,8 +1363,6 @@
"options": "Warehouse",
"print_hide": 1,
"print_width": "50px",
- "show_days": 1,
- "show_seconds": 1,
"width": "50px"
},
{
@@ -1680,8 +1374,6 @@
"options": "Warehouse",
"print_hide": 1,
"print_width": "50px",
- "show_days": 1,
- "show_seconds": 1,
"width": "50px"
},
{
@@ -1705,20 +1397,38 @@
"fieldtype": "Check",
"hidden": 1,
"label": "Ignore Default Payment Terms Template",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_details_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Details",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "column_break_147",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "advance_tax",
+ "fieldtype": "Table",
+ "hidden": 1,
+ "label": "Advance Tax",
+ "options": "Advance Tax",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2021-08-17 20:16:12.737743",
+ "modified": "2021-11-25 13:31:02.716727",
"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 a16795e..f4fd1bf 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -1,31 +1,44 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-# -*- 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
+
+import frappe
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
from frappe.model.mapper import get_mapped_doc
-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
+from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
+
+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,
+ get_total_in_party_account_currency,
+ 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
@@ -101,6 +114,9 @@
self.set_status()
self.validate_purchase_receipt_if_update_stock()
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
+ self.reset_default_field_value("set_warehouse", "items", "warehouse")
+ self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
+ self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
def validate_release_date(self):
if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
@@ -414,6 +430,9 @@
self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
+ self.update_advance_tax_references()
+
+ self.process_common_party_accounting()
def make_gl_entries(self, gl_entries=None, from_repost=False):
if not gl_entries:
@@ -457,8 +476,6 @@
self.make_exchange_gain_loss_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
- self.allocate_advance_taxes(gl_entries)
-
gl_entries = make_regional_gl_entries(gl_entries, self)
gl_entries = merge_similar_entries(gl_entries)
@@ -584,7 +601,7 @@
# Amount added through landed-cost-voucher
if landed_cost_entries:
- for account, amount in iteritems(landed_cost_entries[(item.item_code, item.name)]):
+ for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
gl_entries.append(self.get_gl_dict({
"account": account,
"against": item.expense_account,
@@ -714,7 +731,7 @@
"account": self.stock_received_but_not_billed,
"against": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
- "remarks": self.remarks or "Accounting Entry for Stock",
+ "remarks": self.remarks or _("Accounting Entry for Stock"),
"cost_center": self.cost_center,
"project": item.project or self.project
}, item=item)
@@ -922,7 +939,7 @@
"cost_center": tax.cost_center,
"against": self.supplier,
"credit": valuation_tax[tax.name],
- "remarks": self.remarks or "Accounting Entry for Stock"
+ "remarks": self.remarks or _("Accounting Entry for Stock")
}, item=tax))
@property
@@ -1059,6 +1076,7 @@
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+ self.update_advance_tax_references(cancel=1)
def update_project(self):
project_list = []
@@ -1129,7 +1147,16 @@
if not self.apply_tds:
return
- tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
+ if self.apply_tds and not self.get('tax_withholding_category'):
+ self.tax_withholding_category = frappe.db.get_value('Supplier', self.supplier, 'tax_withholding_category')
+
+ if not self.tax_withholding_category:
+ return
+
+ tax_withholding_details, advance_taxes = get_party_tax_withholding_details(self, self.tax_withholding_category)
+
+ # Adjust TDS paid on advances
+ self.allocate_advance_tds(tax_withholding_details, advance_taxes)
if not tax_withholding_details:
return
@@ -1153,16 +1180,47 @@
# calculate totals again after applying TDS
self.calculate_taxes_and_totals()
+ def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
+ self.set('advance_tax', [])
+ for tax in advance_taxes:
+ allocated_amount = 0
+ pending_amount = flt(tax.tax_amount - tax.allocated_amount)
+ if flt(tax_withholding_details.get('tax_amount')) >= pending_amount:
+ tax_withholding_details['tax_amount'] -= pending_amount
+ allocated_amount = pending_amount
+ elif flt(tax_withholding_details.get('tax_amount')) and flt(tax_withholding_details.get('tax_amount')) < pending_amount:
+ allocated_amount = tax_withholding_details['tax_amount']
+ tax_withholding_details['tax_amount'] = 0
+
+ self.append('advance_tax', {
+ 'reference_type': 'Payment Entry',
+ 'reference_name': tax.parent,
+ 'reference_detail': tax.name,
+ 'account_head': tax.account_head,
+ 'allocated_amount': allocated_amount
+ })
+
+ def update_advance_tax_references(self, cancel=0):
+ for tax in self.get('advance_tax'):
+ at = frappe.qb.DocType("Advance Taxes and Charges").as_("at")
+
+ if cancel:
+ frappe.qb.update(at).set(
+ at.allocated_amount, at.allocated_amount - tax.allocated_amount
+ ).where(at.name == tax.reference_detail).run()
+ else:
+ frappe.qb.update(at).set(
+ at.allocated_amount, at.allocated_amount + tax.allocated_amount
+ ).where(at.name == tax.reference_detail).run()
+
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()
+ outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
+ total = get_total_in_party_account_currency(self)
if not status:
if self.docstatus == 2:
@@ -1170,9 +1228,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, total):
self.status = "Overdue"
- elif outstanding_amount > 0 and due_date >= nowdate:
+ elif 0 < outstanding_amount < total:
+ self.status = "Partly Paid"
+ elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"
#Check if outstanding amount is 0 due to debit note issued against invoice
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
index b6467a3..76c9fcd 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'purchase_invoice',
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
index 771b49a..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];
}
- }
+
+ 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.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 37ff52c..aa2408e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -2,21 +2,26 @@
# License: GNU General Public License v3. See license.txt
-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.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.controllers.accounts_controller import get_payment_terms
+from erpnext.controllers.buying_controller import QtyMismatchError
+from erpnext.exceptions import InvalidCurrency
+from erpnext.projects.doctype.project.test_project import make_project
+from erpnext.stock.doctype.item.test_item import create_item
+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"]
@@ -31,6 +36,27 @@
def tearDownClass(self):
unlink_payment_on_cancel_of_invoice(0)
+ def test_purchase_invoice_received_qty(self):
+ """
+ 1. Test if received qty is validated against accepted + rejected
+ 2. Test if received qty is auto set on save
+ """
+ pi = make_purchase_invoice(
+ qty=1,
+ rejected_qty=1,
+ received_qty=3,
+ item_code="_Test Item Home Desktop 200",
+ rejected_warehouse = "_Test Rejected Warehouse - _TC",
+ update_stock=True, do_not_save=True)
+ self.assertRaises(QtyMismatchError, pi.save)
+
+ pi.items[0].received_qty = 0
+ pi.save()
+ self.assertEqual(pi.items[0].received_qty, 2)
+
+ # teardown
+ pi.delete()
+
def test_gl_entries_without_perpetual_inventory(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC")
pi = frappe.copy_doc(test_records[0])
@@ -231,7 +257,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)
@@ -401,8 +429,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 +470,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 +744,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 +825,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")
@@ -803,29 +833,12 @@
pi.shipping_rule = shipping_rule.name
pi.insert()
-
- shipping_amount = 0.0
- for condition in shipping_rule.get("conditions"):
- if not condition.to_value or (flt(condition.from_value) <= pi.net_total <= flt(condition.to_value)):
- shipping_amount = condition.shipping_amount
-
- shipping_charge = {
- "doctype": "Purchase Taxes and Charges",
- "category": "Valuation and Total",
- "charge_type": "Actual",
- "account_head": shipping_rule.account,
- "cost_center": shipping_rule.cost_center,
- "tax_amount": shipping_amount,
- "description": shipping_rule.name,
- "add_deduct_tax": "Add"
- }
- pi.append("taxes", shipping_charge)
pi.save()
self.assertEqual(pi.net_total, 1250)
- self.assertEqual(pi.total_taxes_and_charges, 462.3)
- self.assertEqual(pi.grand_total, 1712.3)
+ self.assertEqual(pi.total_taxes_and_charges, 354.1)
+ self.assertEqual(pi.grand_total, 1604.1)
def test_make_pi_without_terms(self):
pi = make_purchase_invoice(do_not_save=1)
@@ -1133,38 +1146,35 @@
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.apply_tds = 1
- po.tax_withholding_category = 'TDS - 194 - Dividends - Individual'
+ po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000, item='_Test Non Stock Item',
+ posting_date='2021-09-15')
po.save()
po.submit()
- # Update Unrealized Profit / Loss Account which is used as default advance tax account
- frappe.db.set_value('Company', '_Test Company', 'unrealized_profit_loss_account', '_Test Account Excise Duty - _TC')
-
# Create Payment Entry Against the order
payment_entry = get_payment_entry(dt='Purchase Order', dn=po.name)
payment_entry.paid_from = 'Cash - _TC'
+ payment_entry.apply_tax_withholding_amount = 1
+ payment_entry.tax_withholding_category = 'TDS - 194 - Dividends - Individual'
payment_entry.save()
payment_entry.submit()
# Check GLE for Payment Entry
expected_gle = [
- ['_Test Account Excise Duty - _TC', 3000, 0],
['Cash - _TC', 0, 27000],
- ['Creditors - _TC', 27000, 0],
+ ['Creditors - _TC', 30000, 0],
['TDS Payable - _TC', 0, 3000],
]
@@ -1190,9 +1200,7 @@
# Zero net effect on final TDS Payable on invoice
expected_gle = [
['_Test Account Cost for Goods Sold - _TC', 30000],
- ['_Test Account Excise Duty - _TC', -3000],
- ['Creditors - _TC', -27000],
- ['TDS Payable - _TC', 0]
+ ['Creditors - _TC', -30000]
]
gl_entries = frappe.db.sql("""select account, sum(debit - credit) as amount
@@ -1205,6 +1213,14 @@
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.amount)
+ payment_entry.load_from_db()
+ self.assertEqual(payment_entry.taxes[0].allocated_amount, 3000)
+
+ purchase_invoice.cancel()
+
+ payment_entry.load_from_db()
+ self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)
+
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
from `tabGL Entry`
@@ -1217,16 +1233,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/__init__.py b/erpnext/accounts/doctype/purchase_invoice_advance/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/purchase_invoice_advance/__init__.py
+++ b/erpnext/accounts/doctype/purchase_invoice_advance/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 d157837..44e1f6d 100644
--- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py
+++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class PurchaseInvoiceAdvance(Document):
pass
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/__init__.py b/erpnext/accounts/doctype/purchase_invoice_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/__init__.py
+++ b/erpnext/accounts/doctype/purchase_invoice_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 a7618e2..f9b2efd 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",
@@ -21,10 +22,10 @@
"received_qty",
"qty",
"rejected_qty",
- "stock_uom",
"col_break2",
"uom",
"conversion_factor",
+ "stock_uom",
"stock_qty",
"sec_break1",
"price_list_rate",
@@ -174,7 +175,8 @@
{
"fieldname": "received_qty",
"fieldtype": "Float",
- "label": "Received Qty"
+ "label": "Received Qty",
+ "read_only": 1
},
{
"bold": 1,
@@ -222,7 +224,7 @@
{
"fieldname": "stock_qty",
"fieldtype": "Float",
- "label": "Stock Qty",
+ "label": "Accepted Qty in Stock UOM",
"print_hide": 1,
"read_only": 1,
"reqd": 1
@@ -857,15 +859,23 @@
"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:48.506639",
+ "modified": "2021-11-15 17:04:07.191013",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
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..0e3c723 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class PurchaseInvoiceItem(Document):
pass
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/__init__.py b/erpnext/accounts/doctype/purchase_taxes_and_charges/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges/__init__.py
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 5854dde..262e7eb 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class PurchaseTaxesandCharges(Document):
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..f5eb404 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
@@ -1,12 +1,14 @@
-# -*- coding: utf-8 -*-
# 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.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 db9793d..3176556 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
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..b5b4a67 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
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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..b70179a 100644
--- a/erpnext/accounts/doctype/salary_component_account/salary_component_account.py
+++ b/erpnext/accounts/doctype/salary_component_account/salary_component_account.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class SalaryComponentAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/sales_invoice/__init__.py b/erpnext/accounts/doctype/sales_invoice/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/sales_invoice/__init__.py
+++ b/erpnext/accounts/doctype/sales_invoice/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 2dd3d69..39dfd8d 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -12,6 +12,22 @@
}
company() {
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
+
+ let me = this;
+ if (this.frm.doc.company) {
+ frappe.call({
+ method:
+ "erpnext.accounts.party.get_party_account",
+ args: {
+ party_type: 'Customer',
+ party: this.frm.doc.customer,
+ company: this.frm.doc.company
+ },
+ callback: (response) => {
+ if (response) me.frm.set_value("debit_to", response.message);
+ },
+ });
+ }
}
onload() {
var me = this;
@@ -446,15 +462,6 @@
}
currency() {
- this._super();
- $.each(cur_frm.doc.timesheets, function(i, d) {
- let row = frappe.get_doc(d.doctype, d.name)
- set_timesheet_detail_rate(row.doctype, row.name, cur_frm.doc.currency, row.timesheet_detail)
- });
- calculate_total_billing_amount(cur_frm)
- }
-
- currency() {
var me = this;
super.currency();
if (this.frm.doc.timesheets) {
@@ -462,7 +469,7 @@
let row = frappe.get_doc(d.doctype, d.name)
set_timesheet_detail_rate(row.doctype, row.name, me.frm.doc.currency, row.timesheet_detail)
});
- calculate_total_billing_amount(this.frm);
+ frm.trigger("calculate_timesheet_totals");
}
}
};
@@ -509,15 +516,6 @@
}
}
-// project name
-//--------------------------
-cur_frm.fields_dict['project'].get_query = function(doc, cdt, cdn) {
- return{
- query: "erpnext.controllers.queries.get_project_name",
- filters: {'customer': doc.customer}
- }
-}
-
// Income Account in Details Table
// --------------------------------
cur_frm.set_query("income_account", "items", function(doc) {
@@ -578,6 +576,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: {
@@ -731,19 +732,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;
},
@@ -854,25 +842,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_name': row.project_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"),
@@ -881,8 +936,8 @@
"reqd": 1,
},
{
- fieldtype: 'Column Break',
- fieldname: 'col_break_1',
+ fieldtype: "Column Break",
+ fieldname: "col_break_1",
},
{
"label" : __("To"),
@@ -899,52 +954,22 @@
},
],
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) {
- frm.set_df_property('return_against', 'label', 'Adjustment Against');
+ frm.set_df_property('return_against', 'label', __('Adjustment Against'));
}
if (frappe.boot.active_domains.includes("Healthcare")) {
@@ -954,10 +979,10 @@
if (cint(frm.doc.docstatus==0) && cur_frm.page.current_view_name!=="pos" && !frm.doc.is_return) {
frm.add_custom_button(__('Healthcare Services'), function() {
get_healthcare_services_to_invoice(frm);
- },"Get Items From");
+ },__("Get Items From"));
frm.add_custom_button(__('Prescriptions'), function() {
get_drugs_to_invoice(frm);
- },"Get Items From");
+ },__("Get Items From"));
}
}
else {
@@ -973,26 +998,20 @@
frm: frm
});
},
+
create_dunning: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
frm: frm
});
}
-})
+});
-var calculate_total_billing_amount = function(frm) {
- var doc = frm.doc;
-
- doc.total_billing_amount = 0.0
- if (doc.timesheets) {
- doc.timesheets.forEach((d) => {
- doc.total_billing_amount += flt(d.billing_amount)
- });
+frappe.ui.form.on("Sales Invoice Timesheet", {
+ timesheets_remove(frm) {
+ frm.trigger("calculate_timesheet_totals");
}
-
- refresh_field('total_billing_amount')
-}
+});
var set_timesheet_detail_rate = function(cdt, cdn, currency, timelog) {
frappe.call({
@@ -1039,276 +1058,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 d8aa32e..545abf7 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",
@@ -123,6 +124,13 @@
"total_advance",
"outstanding_amount",
"disable_rounded_total",
+ "column_break4",
+ "write_off_amount",
+ "base_write_off_amount",
+ "write_off_outstanding_amount_automatically",
+ "column_break_74",
+ "write_off_account",
+ "write_off_cost_center",
"advances_section",
"allocate_advances_automatically",
"get_advances",
@@ -143,13 +151,6 @@
"column_break_90",
"change_amount",
"account_for_change_amount",
- "column_break4",
- "write_off_amount",
- "base_write_off_amount",
- "write_off_outstanding_amount_automatically",
- "column_break_74",
- "write_off_account",
- "write_off_cost_center",
"terms_section_break",
"tc_name",
"terms",
@@ -160,14 +161,14 @@
"column_break_84",
"language",
"more_information",
+ "status",
"inter_company_invoice_reference",
- "is_internal_customer",
"represents_company",
"customer_group",
"campaign",
- "is_discounted",
"col_break23",
- "status",
+ "is_internal_customer",
+ "is_discounted",
"source",
"more_info",
"debit_to",
@@ -181,6 +182,7 @@
"sales_team_section_break",
"sales_partner",
"column_break10",
+ "amount_eligible_for_commission",
"commission_rate",
"total_commission",
"section_break2",
@@ -695,7 +697,6 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Scan Barcode",
- "length": 1,
"options": "Barcode"
},
{
@@ -727,6 +728,7 @@
"read_only": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"hide_days": 1,
@@ -736,6 +738,7 @@
"print_hide": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packed_items",
"fieldtype": "Table",
"hide_days": 1,
@@ -1650,7 +1653,7 @@
"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
},
@@ -1952,6 +1955,7 @@
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
"read_only": 1
@@ -2009,6 +2013,19 @@
"hidden": 1,
"label": "Ignore Default Payment Terms Template",
"read_only": 1
+ },
+ {
+ "fieldname": "total_billing_hours",
+ "fieldtype": "Float",
+ "label": "Total Billing Hours",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "amount_eligible_for_commission",
+ "fieldtype": "Currency",
+ "label": "Amount Eligible for Commission",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
@@ -2021,11 +2038,12 @@
"link_fieldname": "consolidated_invoice"
}
],
- "modified": "2021-08-25 14:46:05.279588",
+ "modified": "2021-10-21 20:19:38.667508",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
"name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -2075,4 +2093,4 @@
"title_field": "title",
"track_changes": 1,
"track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 1cf0df0..64712b5 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1,34 +1,48 @@
# 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, 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 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 erpnext.healthcare.utils import manage_invoice_submit_cancel
-
-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,
+ make_depreciation_entry,
+)
+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"
@@ -141,6 +155,8 @@
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points and not self.is_consolidated:
validate_loyalty_points(self, self.loyalty_points)
+ self.reset_default_field_value("set_warehouse", "items", "warehouse")
+
def validate_fixed_asset(self):
for d in self.get("items"):
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
@@ -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):
@@ -333,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')
@@ -479,8 +481,8 @@
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_item_details, get_pos_profile
- if not self.pos_profile:
+ 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
@@ -532,7 +534,7 @@
for item in self.get("items"):
if item.get('item_code'):
profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos, update_data=True)
- for fname, val in iteritems(profile_details):
+ for fname, val in profile_details.items():
if (not for_validate) or (for_validate and not item.get(fname)):
item.set(fname, val)
@@ -638,7 +640,7 @@
return
prev_doc_field_map = {'Sales Order': ['so_required', 'is_pos'],'Delivery Note': ['dn_required', 'update_stock']}
- for key, value in iteritems(prev_doc_field_map):
+ for key, value in prev_doc_field_map.items():
if frappe.db.get_single_value('Selling Settings', value[0]) == 'Yes':
if frappe.get_value('Customer', self.customer, value[0]):
@@ -753,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,
@@ -764,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`
@@ -843,8 +844,6 @@
self.make_exchange_gain_loss_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
- self.allocate_advance_taxes(gl_entries)
-
self.make_item_gl_entries(gl_entries)
self.make_discount_gl_entries(gl_entries)
@@ -933,6 +932,7 @@
asset.db_set("disposal_date", None)
if asset.calculate_depreciation:
+ self.reverse_depreciation_entry_made_after_sale(asset)
self.reset_depreciation_schedule(asset)
else:
@@ -996,22 +996,20 @@
def depreciate_asset(self, asset):
asset.flags.ignore_validate_update_after_submit = True
- asset.prepare_depreciation_data(self.posting_date)
+ asset.prepare_depreciation_data(date_of_sale=self.posting_date)
asset.save()
- post_depreciation_entries(self.posting_date)
+ make_depreciation_entry(asset.name, self.posting_date)
def reset_depreciation_schedule(self, asset):
asset.flags.ignore_validate_update_after_submit = True
# recreate original depreciation schedule of the asset
- asset.prepare_depreciation_data()
+ asset.prepare_depreciation_data(date_of_return=self.posting_date)
self.modify_depreciation_schedule_for_asset_repairs(asset)
asset.save()
- self.delete_depreciation_entry_made_after_sale(asset)
-
def modify_depreciation_schedule_for_asset_repairs(self, asset):
asset_repairs = frappe.get_all(
'Asset Repair',
@@ -1025,7 +1023,7 @@
asset_repair.modify_depreciation_schedule()
asset.prepare_depreciation_data()
- def delete_depreciation_entry_made_after_sale(self, asset):
+ def reverse_depreciation_entry_made_after_sale(self, asset):
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
@@ -1040,11 +1038,19 @@
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):
+ if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \
+ or self.sale_happens_in_the_future(posting_date_of_original_invoice):
+
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
reverse_journal_entry.posting_date = nowdate()
+ frappe.flags.is_reverse_depr_entry = True
reverse_journal_entry.submit()
+ frappe.flags.is_reverse_depr_entry = False
+ asset.flags.ignore_validate_update_after_submit = True
+ schedule.journal_entry = None
+ asset.save()
+
def get_posting_date_of_sales_invoice(self):
return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
@@ -1059,6 +1065,12 @@
return True
return False
+ def sale_happens_in_the_future(self, posting_date_of_original_invoice):
+ if posting_date_of_original_invoice > getdate():
+ return True
+
+ return False
+
@property
def enable_discount_accounting(self):
if not hasattr(self, "_enable_discount_accounting"):
@@ -1283,12 +1295,20 @@
serial_nos = item.serial_no or ""
si_serial_nos = set(get_serial_nos(serial_nos))
+ serial_no_diff = si_serial_nos - dn_serial_nos
- if si_serial_nos - dn_serial_nos:
- frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note").format(item.idx))
+ if serial_no_diff:
+ dn_link = frappe.utils.get_link_to_form("Delivery Note", item.delivery_note)
+ serial_no_msg = ", ".join(frappe.bold(d) for d in serial_no_diff)
+
+ msg = _("Row #{0}: The following Serial Nos are not present in Delivery Note {1}:").format(
+ item.idx, dn_link)
+ msg += " " + serial_no_msg
+
+ frappe.throw(msg=msg, title=_("Serial Nos Mismatch"))
if item.serial_no and cint(item.qty) != len(si_serial_nos):
- frappe.throw(_("Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format(
+ frappe.throw(_("Row #{0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format(
item.idx, item.qty, item.item_code, len(si_serial_nos)))
def update_project(self):
@@ -1373,8 +1393,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)
@@ -1409,59 +1431,14 @@
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:
- discounting_status = get_discounting_status(self.name)
+ outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
+ total = get_total_in_party_account_currency(self)
if not status:
if self.docstatus == 2:
@@ -1469,15 +1446,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 discounting_status=='Disbursed':
- self.status = "Overdue and Discounted"
- elif outstanding_amount > 0 and due_date < nowdate:
+ elif is_overdue(self, total):
self.status = "Overdue"
- elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discounting_status=='Disbursed':
- self.status = "Unpaid and Discounted"
- elif outstanding_amount > 0 and due_date >= nowdate:
+ elif 0 < outstanding_amount < total:
+ self.status = "Partly Paid"
+ elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"
- #Check if outstanding amount is 0 due to 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:
@@ -1486,12 +1461,57 @@
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 get_total_in_party_account_currency(doc):
+ total_fieldname = (
+ "grand_total"
+ if doc.disable_rounded_total
+ else "rounded_total"
+ )
+ if doc.party_account_currency != doc.currency:
+ total_fieldname = "base_" + total_fieldname
+
+ return flt(doc.get(total_fieldname), doc.precision(total_fieldname))
+
+def is_overdue(doc, total):
+ outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
+ if outstanding_amount <= 0:
+ return
+
+ today = getdate()
+ if doc.get('is_pos') or not doc.get('payment_schedule'):
+ return getdate(doc.due_date) < today
+
+ # calculate payable amount till date
+ payment_amount_field = (
+ "base_payment_amount"
+ if doc.party_account_currency != doc.currency
+ else "payment_amount"
+ )
+
+ payable_amount = sum(
+ payment.get(payment_amount_field)
+ for payment in doc.payment_schedule
+ if getdate(payment.due_date) < today
+ )
+
+ return (total - outstanding_amount) < payable_amount
+
+
def get_discounting_status(sales_invoice):
status = None
@@ -1966,22 +1986,23 @@
def append_payment(payment_mode):
payment = doc.append('payments', {})
payment.default = payment_mode.default
- payment.mode_of_payment = payment_mode.parent
+ payment.mode_of_payment = payment_mode.mop
payment.account = payment_mode.default_account
payment.type = payment_mode.type
doc.set('payments', [])
invalid_modes = []
- for pos_payment_method in pos_profile.get('payments'):
- pos_payment_method = pos_payment_method.as_dict()
+ mode_of_payments = [d.mode_of_payment for d in pos_profile.get('payments')]
+ mode_of_payments_info = get_mode_of_payments_info(mode_of_payments, doc.company)
- payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
+ for row in pos_profile.get('payments'):
+ payment_mode = mode_of_payments_info.get(row.mode_of_payment)
if not payment_mode:
- invalid_modes.append(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment))
+ invalid_modes.append(get_link_to_form("Mode of Payment", row.mode_of_payment))
continue
- payment_mode[0].default = pos_payment_method.default
- append_payment(payment_mode[0])
+ payment_mode.default = row.default
+ append_payment(payment_mode)
if invalid_modes:
if invalid_modes == 1:
@@ -1997,6 +2018,24 @@
where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""",
{'company': doc.company}, as_dict=1)
+def get_mode_of_payments_info(mode_of_payments, company):
+ data = frappe.db.sql(
+ """
+ select
+ mpa.default_account, mpa.parent as mop, mp.type as type
+ from
+ `tabMode of Payment Account` mpa,`tabMode of Payment` mp
+ where
+ mpa.parent = mp.name and
+ mpa.company = %s and
+ mp.enabled = 1 and
+ mp.name in %s
+ group by
+ mp.name
+ """, (company, mode_of_payments), as_dict=1)
+
+ return {row.get('mop'): row for row in data}
+
def get_mode_of_payment_info(mode_of_payment, company):
return frappe.db.sql("""
select mpa.default_account, mpa.parent, mp.type as type
@@ -2007,7 +2046,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 3238ead..5cdc8da 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'sales_invoice',
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 c3d83c7..6a488ea 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1,32 +1,38 @@
# 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, 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 six import iteritems
+from frappe.utils import add_days, flt, getdate, nowdate
+
+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.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.utils import get_incoming_rate
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.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):
@@ -125,6 +131,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()
@@ -148,10 +155,11 @@
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', {
@@ -336,7 +344,7 @@
# check if item values are calculated
for i, d in enumerate(si.get("items")):
- for k, v in iteritems(expected_values[i]):
+ for k, v in expected_values[i].items():
self.assertEqual(d.get(k), v)
# check net total
@@ -639,7 +647,7 @@
# check if item values are calculated
for i, d in enumerate(si.get("items")):
- for key, val in iteritems(expected_values[i]):
+ for key, val in expected_values[i].items():
self.assertEqual(d.get(key), val)
# check net total
@@ -677,8 +685,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
@@ -944,16 +953,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()
@@ -961,8 +972,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()
@@ -994,8 +1006,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)
@@ -1048,9 +1060,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)
@@ -1073,8 +1085,6 @@
actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
- frappe.db.commit()
-
self.assertEqual(actual_qty_0 - 5, actual_qty_1)
# outgoing_rate
@@ -1140,6 +1150,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"
@@ -1278,8 +1300,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'
@@ -1395,15 +1418,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)
@@ -1565,8 +1595,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")
@@ -1574,28 +1603,12 @@
si.shipping_rule = shipping_rule.name
si.insert()
-
- shipping_amount = 0.0
- for condition in shipping_rule.get("conditions"):
- if not condition.to_value or (flt(condition.from_value) <= si.net_total <= flt(condition.to_value)):
- shipping_amount = condition.shipping_amount
-
- shipping_charge = {
- "doctype": "Sales Taxes and Charges",
- "category": "Valuation and Total",
- "charge_type": "Actual",
- "account_head": shipping_rule.account,
- "cost_center": shipping_rule.cost_center,
- "tax_amount": shipping_amount,
- "description": shipping_rule.name
- }
- si.append("taxes", shipping_charge)
si.save()
self.assertEqual(si.net_total, 1250)
- self.assertEqual(si.total_taxes_and_charges, 577.05)
- self.assertEqual(si.grand_total, 1827.05)
+ self.assertEqual(si.total_taxes_and_charges, 468.85)
+ self.assertEqual(si.grand_total, 1718.85)
@@ -1615,6 +1628,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,
@@ -1766,6 +1780,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")
@@ -1816,23 +1871,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({
@@ -1958,8 +2003,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")
@@ -2092,7 +2168,9 @@
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()
@@ -2110,7 +2188,9 @@
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",
@@ -2139,9 +2219,9 @@
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
enable_discount_accounting(enable=0)
- def test_asset_depreciation_on_sale(self):
+ def test_asset_depreciation_on_sale_with_pro_rata(self):
"""
- Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on Sept 30.
+ Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
"""
create_asset_data()
@@ -2154,7 +2234,7 @@
expected_values = [
["2020-06-30", 1311.48, 1311.48],
["2021-06-30", 20000.0, 21311.48],
- ["2021-09-30", 3966.76, 25278.24]
+ ["2021-09-30", 5041.1, 26352.58]
]
for i, schedule in enumerate(asset.schedules):
@@ -2163,6 +2243,209 @@
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertTrue(schedule.journal_entry)
+ def test_asset_depreciation_on_sale_without_pro_rata(self):
+ """
+ Tests if an Asset set to depreciate yearly on Dec 31, that gets sold on Dec 31 after two years, created an additional depreciation entry on its date of sale.
+ """
+
+ create_asset_data()
+ asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1,
+ available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3,
+ expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-12-31"), submit=1)
+
+ post_depreciation_entries(getdate("2021-09-30"))
+
+ create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-12-31"))
+ asset.load_from_db()
+
+ expected_values = [
+ ["2020-12-31", 30000, 30000],
+ ["2021-12-31", 30000, 60000]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+ self.assertTrue(schedule.journal_entry)
+
+ def test_depreciation_on_return_of_sold_asset(self):
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+ create_asset_data()
+ asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1)
+ post_depreciation_entries(getdate("2021-09-30"))
+
+ si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30"))
+ return_si = make_return_doc("Sales Invoice", si.name)
+ return_si.submit()
+ asset.load_from_db()
+
+ expected_values = [
+ ["2020-06-30", 1311.48, 1311.48, True],
+ ["2021-06-30", 20000.0, 21311.48, True],
+ ["2022-06-30", 20000.0, 41311.48, False],
+ ["2023-06-30", 20000.0, 61311.48, False],
+ ["2024-06-30", 20000.0, 81311.48, False],
+ ["2025-06-06", 18688.52, 100000.0, False]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+ self.assertEqual(schedule.journal_entry, 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.accounts.doctype.party_link.party_link import create_party_link
+ 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
+ party_link = create_party_link("Supplier", supplier, customer)
+
+ # 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 test_sales_commission(self):
+ si = frappe.copy_doc(test_records[0])
+ item = copy.deepcopy(si.get('items')[0])
+ item.update({
+ "qty": 1,
+ "rate": 500,
+ "grant_commission": 1
+ })
+ si.append("items", item)
+
+ # Test valid values
+ for commission_rate, total_commission in ((0, 0), (10, 50), (100, 500)):
+ si.commission_rate = commission_rate
+ si.save()
+ self.assertEqual(si.amount_eligible_for_commission, 500)
+ self.assertEqual(si.total_commission, total_commission)
+
+ # Test invalid values
+ for commission_rate in (101, -1):
+ si.reload()
+ si.commission_rate = commission_rate
+ self.assertRaises(frappe.ValidationError, si.save)
+
+ def test_sales_invoice_submission_post_account_freezing_date(self):
+ frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', add_days(getdate(), 1))
+ si = create_sales_invoice(do_not_save=True)
+ si.posting_date = add_days(getdate(), 1)
+ si.save()
+
+ self.assertRaises(frappe.ValidationError, si.submit)
+ si.posting_date = getdate()
+ si.submit()
+
+ frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+
+ def test_over_billing_case_against_delivery_note(self):
+ '''
+ Test a case where duplicating the item with qty = 1 in the invoice
+ allows overbilling even if it is disabled
+ '''
+ from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+ over_billing_allowance = frappe.db.get_single_value('Accounts Settings', 'over_billing_allowance')
+ frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', 0)
+
+ dn = create_delivery_note()
+ dn.submit()
+
+ si = make_sales_invoice(dn.name)
+ # make a copy of first item and add it to invoice
+ item_copy = frappe.copy_doc(si.items[0])
+ si.append('items', item_copy)
+ si.save()
+
+ with self.assertRaises(frappe.ValidationError) as err:
+ si.submit()
+
+ self.assertTrue("cannot overbill" in str(err.exception).lower())
+
+ frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', over_billing_allowance)
+
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()
si.naming_series = 'INV-2020-.#####'
@@ -2195,6 +2478,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",
@@ -2216,11 +2500,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",
@@ -2240,9 +2525,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",
@@ -2257,11 +2567,6 @@
"pincode": "1100101"
}).insert()
- address.append("links", {
- "link_doctype": "Company",
- "link_name": "_Test Company"
- })
-
address.save()
def make_test_transporter_for_ewaybill():
@@ -2301,7 +2606,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"
@@ -2375,7 +2681,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:
@@ -2472,29 +2779,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_advance/__init__.py b/erpnext/accounts/doctype/sales_invoice_advance/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/sales_invoice_advance/__init__.py
+++ b/erpnext/accounts/doctype/sales_invoice_advance/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 28aeef4..6d4bd46 100644
--- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py
+++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class SalesInvoiceAdvance(Document):
pass
diff --git a/erpnext/accounts/doctype/sales_invoice_item/__init__.py b/erpnext/accounts/doctype/sales_invoice_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/__init__.py
+++ b/erpnext/accounts/doctype/sales_invoice_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 c77076c..ae9ac35 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -47,13 +47,13 @@
"pricing_rules",
"stock_uom_rate",
"is_free_item",
+ "grant_commission",
"section_break_21",
"net_rate",
"net_amount",
"column_break_24",
"base_net_rate",
"base_net_amount",
- "incoming_rate",
"drop_ship",
"delivered_by_supplier",
"accounting",
@@ -81,6 +81,7 @@
"target_warehouse",
"quality_inspection",
"batch_no",
+ "incoming_rate",
"col_break5",
"allow_zero_valuation_rate",
"serial_no",
@@ -807,12 +808,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",
@@ -828,15 +829,23 @@
"fieldtype": "Link",
"label": "Discount Account",
"options": "Account"
+ },
+ {
+ "default": "0",
+ "fieldname": "grant_commission",
+ "fieldtype": "Check",
+ "label": "Grant Commission",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-08-12 20:15:47.668399",
+ "modified": "2021-10-05 12:24:54.968907",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
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..ebeaf8b 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
@@ -1,10 +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 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..d9b1dbe 100644
--- a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
+++ b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
@@ -1,10 +1,9 @@
-# -*- 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.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 c902973..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,12 +7,19 @@
"field_order": [
"activity_type",
"description",
- "billing_hours",
- "billing_amount",
+ "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",
- "project_name",
- "timesheet_detail"
+ "timesheet_detail",
+ "column_break_13",
+ "project_name"
],
"fields": [
{
@@ -65,19 +72,52 @@
"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-06-08 14:43:02.748981",
+ "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..e50b1c2 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class SalesInvoiceTimesheet(Document):
pass
diff --git a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py
index 9796c7b..97489d1 100644
--- a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py
+++ b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class SalesPartnerItem(Document):
pass
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/__init__.py b/erpnext/accounts/doctype/sales_taxes_and_charges/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges/__init__.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 b1de9d8..d412c1b 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class SalesTaxesandCharges(Document):
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..b590944 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
@@ -1,12 +1,19 @@
# 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 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 522e282..bc1fd8e 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
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..7b13c6c 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
test_records = frappe.get_test_records('Sales Taxes and Charges Template')
diff --git a/erpnext/accounts/doctype/share_balance/share_balance.py b/erpnext/accounts/doctype/share_balance/share_balance.py
index bd165cd..4c9ec43 100644
--- a/erpnext/accounts/doctype/share_balance/share_balance.py
+++ b/erpnext/accounts/doctype/share_balance/share_balance.py
@@ -1,10 +1,9 @@
-# -*- 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 ShareBalance(Document):
pass
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.py b/erpnext/accounts/doctype/share_transfer/share_transfer.py
index 3d4543f..b543ad8 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.py
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.py
@@ -1,15 +1,15 @@
-# -*- 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.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):
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..bc3a521 100644
--- a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
+++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
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..80365aa 100644
--- a/erpnext/accounts/doctype/share_type/share_type.py
+++ b/erpnext/accounts/doctype/share_type/share_type.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class ShareType(Document):
pass
diff --git a/erpnext/accounts/doctype/share_type/share_type_dashboard.py b/erpnext/accounts/doctype/share_type/share_type_dashboard.py
index 455b022..d5551d1 100644
--- a/erpnext/accounts/doctype/share_type/share_type_dashboard.py
+++ b/erpnext/accounts/doctype/share_type/share_type_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..b911c98 100644
--- a/erpnext/accounts/doctype/share_type/test_share_type.py
+++ b/erpnext/accounts/doctype/share_type/test_share_type.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..8a0fa85 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder.py
+++ b/erpnext/accounts/doctype/shareholder/shareholder.py
@@ -1,11 +1,13 @@
-# -*- 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.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..c01ac23 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
+++ b/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
@@ -1,8 +1,3 @@
-from __future__ import unicode_literals
-
-from frappe import _
-
-
def get_data():
return {
'fieldname': 'shareholder',
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..376ea71 100644
--- a/erpnext/accounts/doctype/shareholder/test_shareholder.py
+++ b/erpnext/accounts/doctype/shareholder/test_shareholder.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..7e51299 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
@@ -3,11 +3,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/shipping_rule_dashboard.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule_dashboard.py
index 636ee57..fc70621 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule_dashboard.py
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
index abc6ab8..c06dae0 100644
--- a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
+++ b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
@@ -1,10 +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 unittest
import frappe
-import unittest
-from erpnext.accounts.doctype.shipping_rule.shipping_rule import FromGreaterThanToError, ManyBlankToValuesError, OverlappingConditionError
+
+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_condition/shipping_rule_condition.py b/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py
index db6ef11..07f98de 100644
--- a/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py
+++ b/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py
@@ -3,10 +3,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
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..90123c1 100644
--- a/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py
+++ b/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py
@@ -1,10 +1,9 @@
-# -*- 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.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 8bf7b78f..1dae87f 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -1,25 +1,36 @@
-
-# -*- 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
-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):
# update start just before the subscription doc is created
self.update_subscription_period(self.start_date)
- def update_subscription_period(self, date=None):
+ def update_subscription_period(self, date=None, return_date=False):
"""
Subscription period is the period to be billed. This method updates the
beginning of the billing period and end of the billing period.
@@ -27,28 +38,41 @@
The beginning of the billing period is represented in the doctype as
`current_invoice_start` and the end of the billing period is represented
as `current_invoice_end`.
- """
- self.set_current_invoice_start(date)
- self.set_current_invoice_end()
- def set_current_invoice_start(self, date=None):
+ If return_date is True, it wont update the start and end dates.
+ This is implemented to get the dates to check if is_current_invoice_generated
"""
- This sets the date of the beginning of the current billing period.
+ _current_invoice_start = self.get_current_invoice_start(date)
+ _current_invoice_end = self.get_current_invoice_end(_current_invoice_start)
+
+ if return_date:
+ return _current_invoice_start, _current_invoice_end
+
+ self.current_invoice_start = _current_invoice_start
+ self.current_invoice_end = _current_invoice_end
+
+ def get_current_invoice_start(self, date=None):
+ """
+ This returns the date of the beginning of the current billing period.
If the `date` parameter is not given , it will be automatically set as today's
date.
"""
- if self.is_new_subscription() and self.trial_period_end and getdate(self.trial_period_end) > getdate(self.start_date):
- self.current_invoice_start = add_days(self.trial_period_end, 1)
- elif self.trial_period_start and self.is_trialling():
- self.current_invoice_start = self.trial_period_start
- elif date:
- self.current_invoice_start = date
- else:
- self.current_invoice_start = nowdate()
+ _current_invoice_start = None
- def set_current_invoice_end(self):
+ if self.is_new_subscription() and self.trial_period_end and getdate(self.trial_period_end) > getdate(self.start_date):
+ _current_invoice_start = add_days(self.trial_period_end, 1)
+ elif self.trial_period_start and self.is_trialling():
+ _current_invoice_start = self.trial_period_start
+ elif date:
+ _current_invoice_start = date
+ else:
+ _current_invoice_start = nowdate()
+
+ return _current_invoice_start
+
+ def get_current_invoice_end(self, date=None):
"""
- This sets the date of the end of the current billing period.
+ This returns the date of the end of the current billing period.
If the subscription is in trial period, it will be set as the end of the
trial period.
@@ -57,44 +81,47 @@
current billing period where `x` is the billing interval from the
`Subscription Plan` in the `Subscription`.
"""
- if self.is_trialling() and getdate(self.current_invoice_start) < getdate(self.trial_period_end):
- self.current_invoice_end = self.trial_period_end
+ _current_invoice_end = None
+
+ if self.is_trialling() and getdate(date) < getdate(self.trial_period_end):
+ _current_invoice_end = self.trial_period_end
else:
billing_cycle_info = self.get_billing_cycle_data()
if billing_cycle_info:
- if self.is_new_subscription() and getdate(self.start_date) < getdate(self.current_invoice_start):
- self.current_invoice_end = add_to_date(self.start_date, **billing_cycle_info)
+ if self.is_new_subscription() and getdate(self.start_date) < getdate(date):
+ _current_invoice_end = add_to_date(self.start_date, **billing_cycle_info)
# For cases where trial period is for an entire billing interval
- if getdate(self.current_invoice_end) < getdate(self.current_invoice_start):
- self.current_invoice_end = add_to_date(self.current_invoice_start, **billing_cycle_info)
+ if getdate(self.current_invoice_end) < getdate(date):
+ _current_invoice_end = add_to_date(date, **billing_cycle_info)
else:
- self.current_invoice_end = add_to_date(self.current_invoice_start, **billing_cycle_info)
+ _current_invoice_end = add_to_date(date, **billing_cycle_info)
else:
- self.current_invoice_end = get_last_day(self.current_invoice_start)
+ _current_invoice_end = get_last_day(date)
if self.follow_calendar_months:
billing_info = self.get_billing_cycle_and_interval()
billing_interval_count = billing_info[0]['billing_interval_count']
calendar_months = get_calendar_months(billing_interval_count)
calendar_month = 0
- current_invoice_end_month = getdate(self.current_invoice_end).month
- current_invoice_end_year = getdate(self.current_invoice_end).year
+ current_invoice_end_month = getdate(_current_invoice_end).month
+ current_invoice_end_year = getdate(_current_invoice_end).year
for month in calendar_months:
if month <= current_invoice_end_month:
calendar_month = month
if cint(calendar_month - billing_interval_count) <= 0 and \
- getdate(self.current_invoice_start).month != 1:
+ getdate(date).month != 1:
calendar_month = 12
current_invoice_end_year -= 1
- self.current_invoice_end = get_last_day(cstr(current_invoice_end_year) + '-' \
- + cstr(calendar_month) + '-01')
+ _current_invoice_end = get_last_day(cstr(current_invoice_end_year) + '-' + cstr(calendar_month) + '-01')
- if self.end_date and getdate(self.current_invoice_end) > getdate(self.end_date):
- self.current_invoice_end = self.end_date
+ if self.end_date and getdate(_current_invoice_end) > getdate(self.end_date):
+ _current_invoice_end = self.end_date
+
+ return _current_invoice_end
@staticmethod
def validate_plans_billing_cycle(billing_cycle_data):
@@ -386,6 +413,7 @@
invoice.flags.ignore_mandatory = True
+ invoice.set_missing_values()
invoice.save()
if self.submit_invoice:
@@ -471,10 +499,13 @@
# Check invoice dates and make sure it doesn't have outstanding invoices
return getdate() >= getdate(self.current_invoice_start)
- def is_current_invoice_generated(self):
+ def is_current_invoice_generated(self, _current_start_date=None, _current_end_date=None):
invoice = self.get_current_invoice()
- if invoice and getdate(self.current_invoice_start) <= getdate(invoice.posting_date) <= getdate(self.current_invoice_end):
+ if not (_current_start_date and _current_end_date):
+ _current_start_date, _current_end_date = self.update_subscription_period(date=add_days(self.current_invoice_end, 1), return_date=True)
+
+ if invoice and getdate(_current_start_date) <= getdate(invoice.posting_date) <= getdate(_current_end_date):
return True
return False
@@ -488,13 +519,16 @@
2. Change the `Subscription` status to 'Past Due Date'
3. Change the `Subscription` status to 'Cancelled'
"""
- if getdate() > getdate(self.current_invoice_end) and self.is_prepaid_to_invoice():
- self.update_subscription_period(add_days(self.current_invoice_end, 1))
- if not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
+ if not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \
+ and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
+
prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
self.generate_invoice(prorate)
+ if getdate() > getdate(self.current_invoice_end) and self.is_prepaid_to_invoice():
+ self.update_subscription_period(add_days(self.current_invoice_end, 1))
+
if self.cancel_at_period_end and getdate() > getdate(self.current_invoice_end):
self.cancel_subscription_at_period_end()
@@ -531,11 +565,14 @@
self.update_subscription_period(add_days(self.current_invoice_end, 1))
# Generate invoices periodically even if current invoice are unpaid
- if self.generate_new_invoices_past_due_date and not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice()
- or self.is_prepaid_to_invoice()):
+ if self.generate_new_invoices_past_due_date and not \
+ self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \
+ and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
+
prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
self.generate_invoice(prorate)
+
@staticmethod
def is_paid(invoice):
"""
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 4f2cf48..9dd370b 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -1,14 +1,22 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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)
+
+test_dependencies = ("UOM", "Item Group", "Item")
def create_plan():
if not frappe.db.exists('Subscription Plan', '_Test Plan Name'):
@@ -59,7 +67,6 @@
supplier.insert()
class TestSubscription(unittest.TestCase):
-
def setUp(self):
create_plan()
diff --git a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py
index 6f459b4..41f7f9f 100644
--- a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py
+++ b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py
@@ -1,9 +1,9 @@
-# -*- 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 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..5dc9e32 100644
--- a/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.py
+++ b/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestSubscriptionInvoice(unittest.TestCase):
pass
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 a341c2a..1285343 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
@@ -1,14 +1,15 @@
-# -*- 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.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()
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py
index df30233..d076e39 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..3b6ab60 100644
--- a/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.py
+++ b/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..d22a73f 100644
--- a/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.py
+++ b/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.py
@@ -1,9 +1,9 @@
-# -*- 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 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..12580db 100644
--- a/erpnext/accounts/doctype/subscription_settings/subscription_settings.py
+++ b/erpnext/accounts/doctype/subscription_settings/subscription_settings.py
@@ -1,9 +1,9 @@
-# -*- 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 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..63eae3b 100644
--- a/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.py
+++ b/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestSubscriptionSettings(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py
index de0444e..61c16fe 100644
--- a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py
+++ b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class SupplierGroupItem(Document):
pass
diff --git a/erpnext/accounts/doctype/supplier_item/supplier_item.py b/erpnext/accounts/doctype/supplier_item/supplier_item.py
index ad66e23..2105b1d 100644
--- a/erpnext/accounts/doctype/supplier_item/supplier_item.py
+++ b/erpnext/accounts/doctype/supplier_item/supplier_item.py
@@ -4,5 +4,6 @@
# 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..18cf72a 100644
--- a/erpnext/accounts/doctype/tax_category/tax_category.py
+++ b/erpnext/accounts/doctype/tax_category/tax_category.py
@@ -1,10 +1,9 @@
-# -*- 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 TaxCategory(Document):
pass
diff --git a/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py b/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py
index d643efb..4bdb70a 100644
--- a/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py
+++ b/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..e3b3430 100644
--- a/erpnext/accounts/doctype/tax_category/test_tax_category.py
+++ b/erpnext/accounts/doctype/tax_category/test_tax_category.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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 5814231..a16377c 100644
--- a/erpnext/accounts/doctype/tax_rule/tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py
@@ -1,20 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# 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
-from six import iteritems
+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 erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
+
class IncorrectCustomerGroup(frappe.ValidationError): pass
class IncorrectSupplierType(frappe.ValidationError): pass
@@ -149,7 +147,7 @@
if 'tax_category' in args.keys():
del args['tax_category']
- for key, value in iteritems(args):
+ for key, value in args.items():
if key=="use_for_shopping_cart":
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
elif key == 'customer_group':
@@ -170,6 +168,10 @@
for key in args:
if rule.get(key): rule.no_of_keys_matched += 1
+ def cmp(a, b):
+ # refernce: https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
+ return int(a > b) - int(a < b)
+
rule = sorted(tax_rule,
key = functools.cmp_to_key(lambda b, a:
cmp(a.no_of_keys_matched, b.no_of_keys_matched) or
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..d5ac9b2 100644
--- a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
@@ -1,17 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+
+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
@@ -174,7 +174,7 @@
tax_rule = frappe.new_doc("Tax Rule")
- for key, val in iteritems(args):
+ for key, val in args.items():
if key != "save":
tax_rule.set(key, val)
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..c8d9d45 100644
--- a/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.py
+++ b/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.py
@@ -1,10 +1,9 @@
-# -*- 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 TaxWithholdingAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js
index b8d6c9a..7b47974 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js
@@ -8,7 +8,8 @@
if (child.company) {
return {
filters: {
- 'company': child.company
+ 'company': child.company,
+ 'root_type': ['in', ['Asset', 'Liability']]
}
};
}
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 0cb872c..5bb9b93 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -1,16 +1,41 @@
-# -*- 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.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_accounts()
+ 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_accounts(self):
+ existing_accounts = []
+ for d in self.get('accounts'):
+ if d.get('account') in existing_accounts:
+ frappe.throw(_("Account {0} added multiple times").format(frappe.bold(d.get('account'))))
+
+ existing_accounts.append(d.get('account'))
+
+ def validate_thresholds(self):
+ for d in self.get('rates'):
+ if d.cumulative_threshold and d.cumulative_threshold < d.single_threshold:
+ frappe.throw(_("Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold").format(d.idx))
def get_party_details(inv):
party_type, party = '', ''
@@ -31,15 +56,24 @@
pan_no = ''
parties = []
party_type, party = get_party_details(inv)
+ has_pan_field = frappe.get_meta(party_type).has_field("pan")
if not tax_withholding_category:
- tax_withholding_category, pan_no = frappe.db.get_value(party_type, party, ['tax_withholding_category', 'pan'])
+ if has_pan_field:
+ fields = ['tax_withholding_category', 'pan']
+ else:
+ fields = ['tax_withholding_category']
+
+ tax_withholding_details = frappe.db.get_value(party_type, party, fields, as_dict=1)
+
+ tax_withholding_category = tax_withholding_details.get('tax_withholding_category')
+ pan_no = tax_withholding_details.get('pan')
if not tax_withholding_category:
return
# if tax_withholding_category passed as an argument but not pan_no
- if not pan_no:
+ if not pan_no and has_pan_field:
pan_no = frappe.db.get_value(party_type, party, 'pan')
# Get others suppliers with the same PAN No
@@ -49,8 +83,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}')
@@ -61,10 +95,10 @@
frappe.throw(_('Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.')
.format(tax_withholding_category, inv.company, party))
- tax_amount, tax_deducted = get_tax_amount(
+ tax_amount, tax_deducted, tax_deducted_on_advances = get_tax_amount(
party_type, parties,
inv, tax_details,
- fiscal_year, pan_no
+ posting_date, pan_no
)
if party_type == 'Supplier':
@@ -72,18 +106,24 @@
else:
tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
- return tax_row
+ if inv.doctype == 'Purchase Invoice':
+ return tax_row, tax_deducted_on_advances
+ else:
+ 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 +132,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 +180,43 @@
"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,
+ 'tax_withholding_category': tax_details.tax_withholding_category,
+ 'valid_from': ('>=', tax_details.from_date),
+ 'valid_upto': ('<=', tax_details.to_date)
+ }, 'name')
+
if ldc_name:
return frappe.get_doc('Lower Deduction Certificate', ldc_name)
-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_on_advances = 0
+
+ if inv.doctype == 'Purchase Invoice':
+ tax_deducted_on_advances = get_taxes_deducted_on_advances_allocated(inv, tax_details)
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 +225,50 @@
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
+ if cint(tax_details.round_off_tax_amount):
+ tax_amount = round(tax_amount)
-def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'):
+ return tax_amount, tax_deducted, tax_deducted_on_advances
+
+def get_invoice_vouchers(parties, tax_details, company, party_type='Supplier'):
dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
+ doctype = 'Purchase Invoice' if party_type == 'Supplier' else 'Sales Invoice'
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 +281,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,23 +288,47 @@
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_taxes_deducted_on_advances_allocated(inv, tax_details):
+ advances = [d.reference_name for d in inv.get('advances')]
+ tax_info = []
+
+ if advances:
+ pe = frappe.qb.DocType("Payment Entry").as_("pe")
+ at = frappe.qb.DocType("Advance Taxes and Charges").as_("at")
+
+ tax_info = frappe.qb.from_(at).inner_join(pe).on(
+ pe.name == at.parent
+ ).select(
+ at.parent, at.name, at.tax_amount, at.allocated_amount
+ ).where(
+ pe.tax_withholding_category == tax_details.get('tax_withholding_category')
+ ).where(
+ at.parent.isin(advances)
+ ).where(
+ at.account_head == tax_details.account_head
+ ).run(as_dict=True)
+
+ return tax_info
+
+
+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),
+ 'name': ('in', vouchers),
'docstatus': 1,
'apply_tds': 1
}
@@ -261,7 +349,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)
@@ -284,14 +372,10 @@
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', {
@@ -310,21 +394,21 @@
}, '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)
current_invoice_total = get_invoice_total_without_tcs(inv, tax_details)
total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt
- if ((cumulative_threshold and total_invoiced_amt >= cumulative_threshold)):
+ if (cumulative_threshold and total_invoiced_amt >= cumulative_threshold):
chargeable_amt = total_invoiced_amt - cumulative_threshold
tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0
@@ -336,7 +420,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),
@@ -353,14 +437,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..256d4ac 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,8 +1,3 @@
-from __future__ import unicode_literals
-
-from frappe import _
-
-
def get_data():
return {
'fieldname': 'tax_withholding_category',
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 0f921db..a3fcf7d 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
@@ -1,13 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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"]
@@ -175,6 +174,29 @@
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']],
@@ -250,7 +272,8 @@
def create_records():
# create a new suppliers
- for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3', 'Test TDS Supplier4']:
+ 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
@@ -312,16 +335,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
@@ -338,7 +361,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
@@ -356,7 +380,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
@@ -376,7 +401,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
@@ -386,3 +412,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..16cbccc 100644
--- a/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
+++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
@@ -1,10 +1,9 @@
-# -*- 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 TaxWithholdingRate(Document):
pass
diff --git a/erpnext/accounts/doctype/territory_item/territory_item.py b/erpnext/accounts/doctype/territory_item/territory_item.py
index d46edc9..bcc02be 100644
--- a/erpnext/accounts/doctype/territory_item/territory_item.py
+++ b/erpnext/accounts/doctype/territory_item/territory_item.py
@@ -4,5 +4,6 @@
# 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 3126138..1836db6 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -1,13 +1,18 @@
# 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, 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
@@ -68,8 +73,28 @@
flt(entry.debit_in_account_currency) - flt(entry.credit_in_account_currency)
entry.credit_in_account_currency = 0.0
+ update_net_values(entry)
+
return gl_map
+def update_net_values(entry):
+ # In some scenarios net value needs to be shown in the ledger
+ # This method updates net values as debit or credit
+ if entry.post_net_value and entry.debit and entry.credit:
+ if entry.debit > entry.credit:
+ entry.debit = entry.debit - entry.credit
+ entry.debit_in_account_currency = entry.debit_in_account_currency \
+ - entry.credit_in_account_currency
+ entry.credit = 0
+ entry.credit_in_account_currency = 0
+ else:
+ entry.credit = entry.credit - entry.debit
+ entry.credit_in_account_currency = entry.credit_in_account_currency \
+ - entry.debit_in_account_currency
+
+ entry.debit = 0
+ entry.debit_in_account_currency = 0
+
def merge_similar_entries(gl_map, precision=None):
merged_gl_map = []
accounting_dimensions = get_accounting_dimensions()
@@ -278,13 +303,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 (frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == 'Administrator'):
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
def set_as_cancel(voucher_type, voucher_no):
diff --git a/erpnext/accounts/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..02e3e93 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,3 @@
-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\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/page/__init__.py b/erpnext/accounts/page/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/accounts/page/__init__.py
+++ b/erpnext/accounts/page/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index de7dde9..6b4b43d 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -1,22 +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 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, 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
@@ -53,10 +68,12 @@
party_details["tax_category"] = get_address_tax_category(party.get("tax_category"),
party_address, shipping_address if party_type != "Supplier" else party_address)
- if not party_details.get("taxes_and_charges"):
- party_details["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company,
- customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category,
- billing_address=party_address, shipping_address=shipping_address)
+ tax_template = set_taxes(party.name, party_type, posting_date, company,
+ customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category,
+ billing_address=party_address, shipping_address=shipping_address)
+
+ if tax_template:
+ party_details['taxes_and_charges'] = tax_template
if cint(fetch_payment_terms_template):
party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company)
@@ -68,7 +85,8 @@
if party_type=="Customer":
party_details["sales_team"] = [{
"sales_person": d.sales_person,
- "allocated_percentage": d.allocated_percentage or None
+ "allocated_percentage": d.allocated_percentage or None,
+ "commission_rate": d.commission_rate
} for d in party.get("sales_team")]
# supplier tax withholding category
@@ -203,7 +221,7 @@
return out
@frappe.whitelist()
-def get_party_account(party_type, party, company=None):
+def get_party_account(party_type, party=None, company=None):
"""Returns the account for the given `party`.
Will first search in party (Customer / Supplier) record, if not found,
will search in group (Customer Group / Supplier Group),
@@ -211,8 +229,11 @@
if not company:
frappe.throw(_("Please select a Company"))
- if not party:
- return
+ if not party and party_type in ['Customer', 'Supplier']:
+ default_account_name = "default_receivable_account" \
+ if party_type=="Customer" else "default_payable_account"
+
+ return frappe.get_cached_value('Company', company, default_account_name)
account = frappe.db.get_value("Party Account",
{"parenttype": party_type, "parent": party, "company": company}, "account")
@@ -386,7 +407,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
@@ -492,7 +513,7 @@
timeline_items = dict(data)
- for date, count in iteritems(timeline_items):
+ for date, count in timeline_items.items():
timestamp = get_timestamp(date)
out.update({ timestamp: count })
@@ -648,7 +669,7 @@
if out:
try:
return out[0][0]
- except:
+ except Exception:
return None
else:
return None
diff --git a/erpnext/healthcare/doctype/lab_test_template/__init__.py b/erpnext/accounts/print_format_field_template/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/lab_test_template/__init__.py
rename to erpnext/accounts/print_format_field_template/__init__.py
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__init__.py
copy to erpnext/accounts/print_format_field_template/purchase_invoice_taxes/__init__.py
diff --git a/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json
new file mode 100644
index 0000000..f525f7b
--- /dev/null
+++ b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:06:53.083133",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Purchase Invoice",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:06:53.083133",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__init__.py
copy to erpnext/accounts/print_format_field_template/sales_invoice_taxes/__init__.py
diff --git a/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json
new file mode 100644
index 0000000..8ce62a8
--- /dev/null
+++ b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 17:50:00.152759",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Sales Invoice",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:13:20.894207",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/account_balance/account_balance.py b/erpnext/accounts/report/account_balance/account_balance.py
index be64c32..a2c70a4 100644
--- a/erpnext/accounts/report/account_balance/account_balance.py
+++ b/erpnext/accounts/report/account_balance/account_balance.py
@@ -1,11 +1,13 @@
# 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 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 f5c9449..73370e4 100644
--- a/erpnext/accounts/report/account_balance/test_account_balance.py
+++ b/erpnext/accounts/report/account_balance/test_account_balance.py
@@ -1,10 +1,11 @@
-from __future__ import unicode_literals
+import unittest
import frappe
-import unittest
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):
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js
index b6c6689..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
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.py b/erpnext/accounts/report/accounts_payable/accounts_payable.py
index 246ead6..7b19994 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.py
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.py
@@ -1,10 +1,10 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-import frappe
+
from erpnext.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.py b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py
index c08582b..65fe1de 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py
@@ -1,10 +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
-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 = {
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 1a32e2a..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
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index cedfc0f..a990f23 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -1,13 +1,18 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd.
# 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,10 +105,15 @@
party = gle.party,
posting_date = gle.posting_date,
account_currency = gle.account_currency,
+ remarks = gle.remarks if self.filters.get("show_remarks") else None,
invoiced = 0.0,
paid = 0.0,
credit_note = 0.0,
- outstanding = 0.0
+ outstanding = 0.0,
+ invoiced_in_account_currency = 0.0,
+ paid_in_account_currency = 0.0,
+ credit_note_in_account_currency = 0.0,
+ outstanding_in_account_currency = 0.0
)
self.get_invoices(gle)
@@ -144,21 +154,28 @@
# gle_balance will be the total "debit - credit" for receivable type reports and
# and vice-versa for payable type reports
gle_balance = self.get_gle_balance(gle)
+ gle_balance_in_account_currency = self.get_gle_balance_in_account_currency(gle)
+
if gle_balance > 0:
if gle.voucher_type in ('Journal Entry', 'Payment Entry') and gle.against_voucher:
# debit against sales / purchase invoice
row.paid -= gle_balance
+ row.paid_in_account_currency -= gle_balance_in_account_currency
else:
# invoice
row.invoiced += gle_balance
+ row.invoiced_in_account_currency += gle_balance_in_account_currency
else:
# payment or credit note for receivables
if self.is_invoice(gle):
# stand alone debit / credit note
row.credit_note -= gle_balance
+ row.credit_note_in_account_currency -= gle_balance_in_account_currency
else:
# advance / unlinked payment or other adjustment
row.paid -= gle_balance
+ row.paid_in_account_currency -= gle_balance_in_account_currency
+
if gle.cost_center:
row.cost_center = str(gle.cost_center)
@@ -210,8 +227,13 @@
# as we can use this to filter out invoices without outstanding
for key, row in self.voucher_balance.items():
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
+ row.outstanding_in_account_currency = flt(row.invoiced_in_account_currency - row.paid_in_account_currency - \
+ row.credit_note_in_account_currency, self.currency_precision)
+
row.invoice_grand_total = row.invoiced
- if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
+
+ if (abs(row.outstanding) > 1.0/10 ** self.currency_precision) and \
+ (abs(row.outstanding_in_account_currency) > 1.0/10 ** self.currency_precision):
# non-zero oustanding, we must consider this row
if self.is_invoice(row) and self.filters.based_on_payment_terms:
@@ -523,7 +545,9 @@
def set_ageing(self, row):
if self.filters.ageing_based_on == "Due Date":
- entry_date = row.due_date
+ # use posting date as a fallback for advances posted via journal and payment entry
+ # when ageing viewed by due date
+ entry_date = row.due_date or row.posting_date
elif self.filters.ageing_based_on == "Supplier Invoice Date":
entry_date = row.bill_date
else:
@@ -577,10 +601,14 @@
else:
select_fields = "debit, credit"
+ doc_currency_fields = "debit_in_account_currency, credit_in_account_currency"
+
+ remarks = ", remarks" if self.filters.get("show_remarks") else ""
+
self.gl_entries = frappe.db.sql("""
select
name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
- against_voucher_type, against_voucher, account_currency, {0}
+ against_voucher_type, against_voucher, account_currency, {0}, {1} {remarks}
from
`tabGL Entry`
where
@@ -588,8 +616,8 @@
and is_cancelled = 0
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)
+ {2} {3} {4}"""
+ .format(select_fields, doc_currency_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"):
@@ -710,6 +738,13 @@
# get the balance of the GL (debit - credit) or reverse balance based on report type
return gle.get(self.dr_or_cr) - self.get_reverse_balance(gle)
+ def get_gle_balance_in_account_currency(self, gle):
+ # get the balance of the GL (debit - credit) or reverse balance based on report type
+ return gle.get(self.dr_or_cr + '_in_account_currency') - self.get_reverse_balance_in_account_currency(gle)
+
+ def get_reverse_balance_in_account_currency(self, gle):
+ return gle.get('debit_in_account_currency' if self.dr_or_cr=='credit' else 'credit_in_account_currency')
+
def get_reverse_balance(self, gle):
# get "credit" balance if report type is "debit" and vice versa
return gle.get('debit' if self.dr_or_cr=='credit' else 'credit')
@@ -748,6 +783,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 cca6760..ab95c93 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -1,11 +1,12 @@
-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):
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 4bfb022..3c94629 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -1,13 +1,14 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
from frappe import _, scrub
-from frappe.utils import flt, cint
+from frappe.utils import cint
+
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 = {
@@ -35,7 +36,7 @@
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {}
- for party, party_dict in iteritems(self.party_total):
+ for party, party_dict in self.party_total.items():
if party_dict.outstanding == 0:
continue
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..98f5b74 100644
--- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
+++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
@@ -1,10 +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, 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..0f9435f 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
@@ -1,10 +1,10 @@
# 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 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 7838385..dc1f7aa 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.py
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py
@@ -1,12 +1,18 @@
# 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 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,
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 95f724c..b456e89 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -1,10 +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
from frappe import _
-from frappe.utils import nowdate, getdate
+from frappe.utils import getdate, nowdate
+
def execute(filters=None):
if not filters: filters = {}
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..6c401fb 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -1,10 +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
-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 2dcea22..1d7463c 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
@@ -1,10 +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):
data = get_data(filters) or []
columns = get_columns()
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 443126e..3bb590a 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -1,9 +1,8 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 _
@@ -48,7 +47,7 @@
return columns, data, None, chart
def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
- for account, monthwise_data in iteritems(dimension_items):
+ for account, monthwise_data in dimension_items.items():
row = [dimension, account]
totals = [0, 0, 0]
for year in get_fiscal_years(filters):
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index 3577457..15041f2 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -1,14 +1,21 @@
# 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 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.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
-from six import iteritems
def execute(filters=None):
@@ -130,9 +137,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')
@@ -193,7 +200,7 @@
def get_report_summary(summary_data, currency):
report_summary = []
- for label, value in iteritems(summary_data):
+ for label, value in summary_data.items():
report_summary.append(
{
"value": value,
diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
index c11c153..45d147e 100644
--- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
@@ -1,12 +1,15 @@
# 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.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 6a8301a..d3e836a 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -92,6 +92,11 @@
"label": __("Include Default Book Entries"),
"fieldtype": "Check",
"default": 1
+ },
+ {
+ "fieldname": "show_zero_values",
+ "label": __("Show zero values"),
+ "fieldtype": "Check"
}
],
"formatter": function(value, row, column, data, default_formatter) {
@@ -103,8 +108,11 @@
column.is_tree = true;
}
- value = default_formatter(value, row, column, data);
+ if (data && data.account && column.apply_currency_formatter) {
+ data.currency = erpnext.get_currency(column.company_name);
+ }
+ value = default_formatter(value, row, column, data);
if (!data.parent_account) {
value = $(`<span>${value}</span>`);
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index fc42127..01799d5 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -1,18 +1,43 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 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 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 (
+ filter_out_zero_value_rows,
+ get_fiscal_year_data,
+ sort_accounts,
+)
+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 +47,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 +78,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 +110,38 @@
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]:
+ if data:
+ account_name = get_root_account_name(data[0].root_type, company)
+ opening_value += (get_opening_balance(account_name, data, company) or 0.0)
+
+ opening_balance[company] = opening_value
+
+ 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,36 +233,43 @@
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
def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, ignore_closing_entries=False):
- accounts, accounts_by_name = get_account_heads(root_type,
+ accounts, accounts_by_name, parent_children_map = get_account_heads(root_type,
companies, filters)
if not accounts: return []
@@ -216,6 +283,8 @@
start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None
end_date = filters.period_end_date
+ filters.end_date = end_date
+
gl_entries_by_account = {}
for root in frappe.db.sql("""select lft, rgt from tabAccount
where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
@@ -224,9 +293,12 @@
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)
+
+ out = filter_out_zero_value_rows(out, parent_children_map, show_zero_values=filters.get("show_zero_values"))
if out:
add_total_row(out, root_type, balance_must_be, companies, company_currency)
@@ -237,19 +309,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 +354,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)
@@ -278,7 +376,7 @@
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
- return accounts, accounts_by_name
+ return accounts, accounts_by_name, parent_children_map
def update_parent_account_names(accounts):
"""Update parent_account_name in accounts list.
@@ -287,7 +385,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 +426,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 +440,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 +461,7 @@
row["has_value"] = has_value
row["total"] = total
+
data.append(row)
return data
@@ -400,7 +509,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 +524,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 +534,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 +580,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 +595,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..56db841 100644
--- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
+++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
@@ -1,12 +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
-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):
@@ -143,9 +142,9 @@
self.party_data[gle.party].paid_amount -= amount
out = []
- for party, row in iteritems(self.party_data):
+ for party, row in self.party_data.items():
if row.opening_balance or row.invoiced_amount or row.paid_amount or row.return_amount or row.closing_amount:
- total_party_adjustment = sum(amount for amount in itervalues(self.party_adjustment_details.get(party, {})))
+ total_party_adjustment = sum(amount for amount in self.party_adjustment_details.get(party, {}).values())
row.paid_amount -= total_party_adjustment
adjustments = self.party_adjustment_details.get(party, {})
@@ -267,7 +266,7 @@
adjustment_voucher_entries.setdefault((gle.voucher_type, gle.voucher_no), [])
adjustment_voucher_entries[(gle.voucher_type, gle.voucher_no)].append(gle)
- for voucher_gl_entries in itervalues(adjustment_voucher_entries):
+ for voucher_gl_entries in adjustment_voucher_entries.values():
parties = {}
accounts = {}
has_irrelevant_entry = False
@@ -287,7 +286,7 @@
if parties and accounts:
if len(parties) == 1:
party = list(parties.keys())[0]
- for account, amount in iteritems(accounts):
+ for account, amount in accounts.items():
self.party_adjustment_accounts.add(account)
self.party_adjustment_details.setdefault(party, {})
self.party_adjustment_details[party].setdefault(account, 0)
@@ -295,7 +294,7 @@
elif len(accounts) == 1 and not has_irrelevant_entry:
account = list(accounts.keys())[0]
self.party_adjustment_accounts.add(account)
- for party, amount in iteritems(parties):
+ for party, amount in parties.items():
self.party_adjustment_details.setdefault(party, {})
self.party_adjustment_details[party].setdefault(account, 0)
self.party_adjustment_details[party][account] += amount
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 9953d8f..004d092 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
@@ -1,11 +1,12 @@
# 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 erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
+
def execute(filters=None):
columns = get_column()
args = get_args()
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..d547470 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
@@ -1,15 +1,18 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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
+
+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)
@@ -103,7 +106,7 @@
def format_gl_entries(gl_entries_by_account, accounts_by_name, dimension_items_list):
- for entries in itervalues(gl_entries_by_account):
+ for entries in gl_entries_by_account.values():
for entry in entries:
d = accounts_by_name.get(entry.account)
if not d:
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 39ff804..1e89b65 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -1,23 +1,22 @@
-# -*- coding: utf-8 -*-
-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-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 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):
@@ -187,7 +186,7 @@
def calculate_values(
accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy):
- for entries in itervalues(gl_entries_by_account):
+ for entries in gl_entries_by_account.values():
for entry in entries:
d = accounts_by_name.get(entry.account)
if not d:
@@ -339,9 +338,9 @@
"""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])
+ return int(a[key] > b[key]) - int(a[key] < b[key])
elif is_root:
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
return -1
@@ -353,7 +352,7 @@
return -1
else:
# sort by key (number) or name
- return cmp(a[key], b[key])
+ return int(a[key] > b[key]) - int(a[key] < b[key])
return 1
accounts.sort(key = functools.cmp_to_key(compare_accounts))
@@ -421,8 +420,7 @@
{additional_conditions}
and posting_date <= %(to_date)s
and is_cancelled = 0
- {distributed_cost_center_query}
- order by account, posting_date""".format(
+ {distributed_cost_center_query}""".format(
additional_conditions=additional_conditions,
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 095f5ed..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",
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 3723c8e..385c8b2 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -1,18 +1,25 @@
# 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, 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, getdate
+
+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
+
+# to cache translations
+TRANSLATIONS = frappe._dict()
+
def execute(filters=None):
if not filters:
return [], []
@@ -37,10 +44,20 @@
columns = get_columns(filters)
+ update_translations()
+
res = get_result(filters, account_details)
return columns, res
+def update_translations():
+ TRANSLATIONS.update(
+ dict(
+ OPENING = _('Opening'),
+ TOTAL = _('Total'),
+ CLOSING_TOTAL = _('Closing (Opening + Total)')
+ )
+ )
def validate_filters(filters, account_details):
if not filters.get("company"):
@@ -55,14 +72,14 @@
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:
@@ -146,8 +163,10 @@
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("group_by") == "Group by Account":
+ order_by_statement = "order by account, posting_date, creation"
if filters.get("include_default_book_entries"):
filters['company_fb'] = frappe.db.get_value("Company",
@@ -305,13 +324,13 @@
# Opening for filtered account
data.append(totals.opening)
- if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
- for acc, acc_dict in iteritems(gle_map):
+ if filters.get("group_by") != 'Group by Voucher (Consolidated)':
+ for acc, acc_dict in gle_map.items():
# 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
@@ -320,7 +339,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:
@@ -344,15 +363,15 @@
credit_in_account_currency=0.0
)
return _dict(
- opening = _get_debit_credit_dict(_('Opening')),
- total = _get_debit_credit_dict(_('Total')),
- closing = _get_debit_credit_dict(_('Closing (Opening + Total)'))
+ opening = _get_debit_credit_dict(TRANSLATIONS.OPENING),
+ total = _get_debit_credit_dict(TRANSLATIONS.TOTAL),
+ closing = _get_debit_credit_dict(TRANSLATIONS.CLOSING_TOTAL)
)
def group_by_field(group_by):
- if group_by == _('Group by Party'):
+ 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'
@@ -371,22 +390,23 @@
entries = []
consolidated_gle = OrderedDict()
group_by = group_by_field(filters.get('group_by'))
+ group_by_voucher_consolidated = filters.get("group_by") == 'Group by Voucher (Consolidated)'
if filters.get('show_net_values_in_party_account'):
account_type_map = get_account_type_map(filters.get('company'))
def update_value_in_dict(data, key, gle):
- data[key].debit += flt(gle.debit)
- data[key].credit += flt(gle.credit)
+ data[key].debit += gle.debit
+ data[key].credit += gle.credit
- data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
- data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
+ data[key].debit_in_account_currency += gle.debit_in_account_currency
+ data[key].credit_in_account_currency += gle.credit_in_account_currency
if filters.get('show_net_values_in_party_account') and \
account_type_map.get(data[key].account) in ('Receivable', 'Payable'):
- net_value = flt(data[key].debit) - flt(data[key].credit)
- net_value_in_account_currency = flt(data[key].debit_in_account_currency) \
- - flt(data[key].credit_in_account_currency)
+ net_value = data[key].debit - data[key].credit
+ net_value_in_account_currency = data[key].debit_in_account_currency \
+ - data[key].credit_in_account_currency
if net_value < 0:
dr_or_cr = 'credit'
@@ -404,21 +424,29 @@
data[key].against_voucher += ', ' + gle.against_voucher
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
- for gle in gl_entries:
- if (gle.posting_date < from_date or
- (cstr(gle.is_opening) == "Yes" and not filters.get("show_opening_entries"))):
- update_value_in_dict(gle_map[gle.get(group_by)].totals, 'opening', gle)
- update_value_in_dict(totals, 'opening', gle)
+ show_opening_entries = filters.get("show_opening_entries")
- update_value_in_dict(gle_map[gle.get(group_by)].totals, 'closing', gle)
+ for gle in gl_entries:
+ group_by_value = gle.get(group_by)
+
+ if (gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries)):
+ if not group_by_voucher_consolidated:
+ update_value_in_dict(gle_map[group_by_value].totals, 'opening', gle)
+ update_value_in_dict(gle_map[group_by_value].totals, 'closing', gle)
+
+ update_value_in_dict(totals, 'opening', gle)
update_value_in_dict(totals, 'closing', gle)
elif gle.posting_date <= to_date:
- update_value_in_dict(gle_map[gle.get(group_by)].totals, 'total', gle)
- update_value_in_dict(totals, 'total', gle)
- if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
- gle_map[gle.get(group_by)].entries.append(gle)
- elif filters.get("group_by") == _('Group by Voucher (Consolidated)'):
+ if not group_by_voucher_consolidated:
+ update_value_in_dict(gle_map[group_by_value].totals, 'total', gle)
+ update_value_in_dict(gle_map[group_by_value].totals, 'closing', gle)
+ update_value_in_dict(totals, 'total', gle)
+ update_value_in_dict(totals, 'closing', gle)
+
+ gle_map[group_by_value].entries.append(gle)
+
+ elif group_by_voucher_consolidated:
keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")]
for dim in accounting_dimensions:
keylist.append(gle.get(dim))
@@ -429,10 +457,9 @@
else:
update_value_in_dict(consolidated_gle, key, gle)
- update_value_in_dict(gle_map[gle.get(group_by)].totals, 'closing', gle)
- update_value_in_dict(totals, 'closing', gle)
-
for key, value in consolidated_gle.items():
+ update_value_in_dict(totals, 'total', value)
+ update_value_in_dict(totals, 'closing', value)
entries.append(value)
return totals, entries
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 8e33af7..b18b940 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
@@ -1,12 +1,15 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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,
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js
index ba17a94..685f2d6 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 || row[1].content == "Total")) {
+ 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 5fff3fd..76c560a 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.json
+++ b/erpnext/accounts/report/gross_profit/gross_profit.json
@@ -9,7 +9,7 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
- "modified": "2021-08-19 18:57:07.468202",
+ "modified": "2021-11-13 19:14:23.730198",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Gross Profit",
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index c949d9b..84effc0 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -1,12 +1,13 @@
# 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 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):
@@ -18,7 +19,7 @@
data = []
group_wise_columns = frappe._dict({
- "invoice": ["parent", "customer", "customer_group", "posting_date","item_code", "item_name","item_group", "brand", "description", \
+ "invoice": ["invoice_or_item", "customer", "customer_group", "posting_date","item_code", "item_name","item_group", "brand", "description",
"warehouse", "qty", "base_rate", "buying_rate", "base_amount",
"buying_amount", "gross_profit", "gross_profit_percent", "project"],
"item_code": ["item_code", "item_name", "brand", "description", "qty", "base_rate",
@@ -41,6 +42,34 @@
columns = get_columns(group_wise_columns, filters)
+ 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)):
@@ -48,15 +77,15 @@
row.append(filters.currency)
if idx == len(gross_profit_data.grouped_data)-1:
- row[0] = frappe.bold("Total")
- data.append(row)
+ row[0] = "Total"
- return columns, data
+ data.append(row)
def get_columns(group_wise_columns, filters):
columns = []
column_map = frappe._dict({
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
+ "invoice_or_item": _("Sales Invoice") + ":Link/Sales Invoice:120",
"posting_date": _("Posting Date") + ":Date:100",
"posting_time": _("Posting Time") + ":Data:100",
"item_code": _("Item Code") + ":Link/Item:100",
@@ -93,12 +122,38 @@
return columns
+def get_column_names():
+ return frappe._dict({
+ 'invoice_or_item': 'sales_invoice',
+ 'customer': 'customer',
+ 'customer_group': 'customer_group',
+ 'posting_date': 'posting_date',
+ 'item_code': 'item_code',
+ 'item_name': 'item_name',
+ 'item_group': 'item_group',
+ 'brand': 'brand',
+ 'description': 'description',
+ 'warehouse': 'warehouse',
+ 'qty': 'qty',
+ 'base_rate': 'avg._selling_rate',
+ 'buying_rate': 'valuation_rate',
+ 'base_amount': 'selling_amount',
+ 'buying_amount': 'buying_amount',
+ 'gross_profit': 'gross_profit',
+ 'gross_profit_percent': 'gross_profit_%',
+ 'project': 'project'
+ })
+
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()
@@ -112,7 +167,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
@@ -134,12 +194,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)
@@ -171,7 +239,7 @@
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)
@@ -179,19 +247,31 @@
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.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 = self.set_average_rate(row)
- self.grouped_data.append(row)
- self.add_to_totals(row)
+ if row.indent == 1.0:
+ if row.parent in self.returned_invoices \
+ and row.item_code in self.returned_invoices[row.parent]:
+ returned_item_rows = self.returned_invoices[row.parent][row.item_code]
+ for returned_item_row in returned_item_rows:
+ row.qty += flt(returned_item_row.qty)
+ row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
+ row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
+ if (flt(row.qty) or row.base_amount):
+ row = self.set_average_rate(row)
+ self.grouped_data.append(row)
+ self.add_to_totals(row)
+
self.set_average_gross_profit(self.totals)
- self.grouped_data.append(self.totals)
+
+ if self.filters.get("group_by") == "Invoice":
+ self.totals.indent = 0.0
+ self.totals.parent_invoice = ""
+ self.totals.invoice_or_item = "Total"
+ self.si_list.append(self.totals)
+ else:
+ 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)
@@ -354,6 +434,111 @@
.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.invoice_or_item = row.item_code
+
+ if frappe.db.exists('Product Bundle', row.item_code):
+ self.add_bundle_items(row, index)
+
+ def get_invoice_row(self, row):
+ return frappe._dict({
+ 'parent_invoice': "",
+ 'indent': 0.0,
+ 'invoice_or_item': row.parent,
+ 'parent': None,
+ 'posting_date': row.posting_date,
+ 'posting_time': row.posting_time,
+ 'project': row.project,
+ 'update_stock': row.update_stock,
+ 'customer': row.customer,
+ 'customer_group': row.customer_group,
+ 'item_code': None,
+ 'item_name': None,
+ 'description': None,
+ 'warehouse': None,
+ 'item_group': None,
+ 'brand': None,
+ 'dn_detail': None,
+ 'delivery_note': None,
+ 'qty': None,
+ 'item_row': None,
+ 'is_return': row.is_return,
+ 'cost_center': row.cost_center,
+ 'base_net_amount': frappe.db.get_value('Sales Invoice', row.parent, 'base_net_total')
+ })
+
+ 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': None,
+ 'invoice_or_item': item.item_code,
+ 'posting_date': product_bundle.posting_date,
+ 'posting_time': product_bundle.posting_time,
+ 'project': product_bundle.project,
+ 'customer': product_bundle.customer,
+ 'customer_group': product_bundle.customer_group,
+ 'item_code': item.item_code,
+ 'item_name': item_name,
+ 'description': description,
+ 'warehouse': product_bundle.warehouse,
+ 'item_group': item_group,
+ 'brand': brand,
+ 'dn_detail': product_bundle.dn_detail,
+ 'delivery_note': product_bundle.delivery_note,
+ 'qty': (flt(product_bundle.qty) * flt(item.qty)),
+ 'item_row': None,
+ 'is_return': product_bundle.is_return,
+ 'cost_center': product_bundle.cost_center
+ })
+
+ def get_bundle_item_details(self, item_code):
+ return frappe.db.get_value(
+ 'Item',
+ item_code,
+ ['item_name', 'description', 'item_group', 'brand']
+ )
+
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..2f23c8e 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
@@ -1,10 +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.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..aaed58d 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
@@ -1,15 +1,23 @@
# 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, 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 c9c22c2..9b35538 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
@@ -1,14 +1,19 @@
# 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, 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)
diff --git a/erpnext/accounts/report/non_billed_report.py b/erpnext/accounts/report/non_billed_report.py
index 5173505..a421bc5 100644
--- a/erpnext/accounts/report/non_billed_report.py
+++ b/erpnext/accounts/report/non_billed_report.py
@@ -1,12 +1,13 @@
# 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 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"
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 556f5ad..6c12093 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
@@ -1,11 +1,12 @@
# 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 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):
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index b7e112c..77e7568 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -1,12 +1,13 @@
# 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 _, _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 [], []
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..882e411 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
@@ -1,12 +1,18 @@
# 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 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.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
index 48bd730..3dcb862 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
@@ -1,11 +1,15 @@
# 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 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.py b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
index ba236b9..406f7a5 100644
--- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
+++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
@@ -1,9 +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 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/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 10edd41..a9696bd 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -1,11 +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
+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 a5eced5..e88675b 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
@@ -1,11 +1,12 @@
# 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 erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
+
def execute(filters=None):
columns = get_column()
args = get_args()
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..966b1d4 100644
--- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py
+++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py
@@ -1,9 +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 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_partners_commission/sales_partners_commission.json b/erpnext/accounts/report/sales_partners_commission/sales_partners_commission.json
index a740de3..9dd4e43 100644
--- a/erpnext/accounts/report/sales_partners_commission/sales_partners_commission.json
+++ b/erpnext/accounts/report/sales_partners_commission/sales_partners_commission.json
@@ -1,27 +1,30 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2013-05-06 12:28:23",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2017-03-06 05:52:57.645281",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Sales Partners Commission",
- "owner": "Administrator",
- "query": "SELECT\n sales_partner as \"Sales Partner:Link/Sales Partner:150\",\n\tsum(base_net_total) as \"Invoiced Amount (Exclusive Tax):Currency:210\",\n\tsum(total_commission) as \"Total Commission:Currency:150\",\n\tsum(total_commission)*100/sum(base_net_total) as \"Average Commission Rate:Currency:170\"\nFROM\n\t`tabSales Invoice`\nWHERE\n\tdocstatus = 1 and ifnull(base_net_total, 0) > 0 and ifnull(total_commission, 0) > 0\nGROUP BY\n\tsales_partner\nORDER BY\n\t\"Total Commission:Currency:120\"",
- "ref_doctype": "Sales Invoice",
- "report_name": "Sales Partners Commission",
- "report_type": "Query Report",
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2013-05-06 12:28:23",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 3,
+ "is_standard": "Yes",
+ "modified": "2021-10-06 06:26:07.881340",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Partners Commission",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "query": "SELECT\n sales_partner as \"Sales Partner:Link/Sales Partner:220\",\n\tsum(base_net_total) as \"Invoiced Amount (Excl. Tax):Currency:220\",\n\tsum(amount_eligible_for_commission) as \"Amount Eligible for Commission:Currency:220\",\n\tsum(total_commission) as \"Total Commission:Currency:170\",\n\tsum(total_commission)*100/sum(amount_eligible_for_commission) as \"Average Commission Rate:Percent:220\"\nFROM\n\t`tabSales Invoice`\nWHERE\n\tdocstatus = 1 and ifnull(base_net_total, 0) > 0 and ifnull(total_commission, 0) > 0\nGROUP BY\n\tsales_partner\nORDER BY\n\t\"Total Commission:Currency:120\"",
+ "ref_doctype": "Sales Invoice",
+ "report_name": "Sales Partners Commission",
+ "report_type": "Query Report",
"roles": [
{
"role": "Accounts Manager"
- },
+ },
{
"role": "Accounts User"
}
]
-}
+}
\ 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..3b73628 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,6 @@
# 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 e4a3d35..b3f6c72 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
@@ -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 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"]
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index f38bd78..a9d0081 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -1,12 +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 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)
diff --git a/erpnext/accounts/report/share_balance/share_balance.py b/erpnext/accounts/report/share_balance/share_balance.py
index 9f22f81..943c4e1 100644
--- a/erpnext/accounts/report/share_balance/share_balance.py
+++ b/erpnext/accounts/report/share_balance/share_balance.py
@@ -1,9 +1,10 @@
# 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 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..b3ff6e4 100644
--- a/erpnext/accounts/report/share_ledger/share_ledger.py
+++ b/erpnext/accounts/report/share_ledger/share_ledger.py
@@ -1,11 +1,10 @@
-# -*- 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.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 fbd25b1..52beeaf 100644
--- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py
+++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.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 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 = {
diff --git a/erpnext/accounts/report/tax_detail/tax_detail.py b/erpnext/accounts/report/tax_detail/tax_detail.py
index 18436de..eeb8483 100644
--- a/erpnext/accounts/report/tax_detail/tax_detail.py
+++ b/erpnext/accounts/report/tax_detail/tax_detail.py
@@ -2,9 +2,10 @@
# For license information, please see license.txt
# 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..bf668ab 100644
--- a/erpnext/accounts/report/tax_detail/test_tax_detail.py
+++ b/erpnext/accounts/report/tax_detail/test_tax_detail.py
@@ -1,16 +1,24 @@
-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..07f2e43 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,12 @@
-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 +14,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 +33,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 +107,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..caee1a1 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -1,25 +1,18 @@
# 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 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 +20,63 @@
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 not tax_withholding_category:
+ tax_withholding_category = supplier_map.get(supplier, {}).get('tax_withholding_category')
+ rate = tax_rate_map.get(tax_withholding_category)
- 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
+ if entry.account in tds_accounts:
+ tds_deducted += (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]
+ total_amount_credited += (entry.credit - entry.debit)
- if rate and len(rate) > 0 and tds_deducted:
- rate = rate[0]
-
- row = [supplier.pan, supplier.name]
+ if tds_deducted:
+ row = {
+ 'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier, {}).get('pan'),
+ 'supplier': supplier_map.get(supplier, {}).get('name')
+ }
if filters.naming_series == 'Naming Series':
- row.append(supplier.supplier_name)
+ row.update({'supplier_name': supplier_map.get(supplier, {}).get('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, {}).get('supplier_type'),
+ 'tds_rate': rate,
+ 'total_amount_credited': total_amount_credited,
+ 'tds_deducted': tds_deducted,
+ 'transaction_date': posting_date,
+ 'transaction_type': voucher_type,
+ 'ref_no': name
+ })
+
out.append(row)
return out
-def get_supplier_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', 'tax_withholding_category'])
- 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 +86,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 +178,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.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 1fc0faa..bda44f6 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -1,13 +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, 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")
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 f034e74..d843dfd 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
@@ -1,12 +1,14 @@
# 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 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)
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 1250d67..26b9389 100644
--- a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py
+++ b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py
@@ -1,10 +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/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index ba461ed..c38e4b8 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -1,9 +1,9 @@
-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 +98,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..effc913 100644
--- a/erpnext/accounts/test/test_utils.py
+++ b/erpnext/accounts/test/test_utils.py
@@ -1,22 +1,50 @@
-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 +56,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 +67,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 +79,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 +91,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 118f628..39e84e3 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -1,21 +1,22 @@
-# -*- coding: utf-8 -*-
# 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, erpnext
+from json import loads
+
+import frappe
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 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 erpnext.stock.utils import get_stock_value_on
+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
@@ -341,31 +342,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):
@@ -374,6 +386,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("""
@@ -395,14 +410,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:
@@ -415,58 +430,45 @@
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")
- 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.update((frappe.copy_doc(jv_detail)).as_dict())
- 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(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.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('credit_in_account_currency' if d['dr_or_cr'] == 'debit_in_account_currency'
+ else 'debit_in_account_currency', 0)
+ new_row.set('credit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'debit', 0)
+
+ new_row.set("reference_type", d["against_voucher_type"])
+ new_row.set("reference_name", d["against_voucher"])
+
+ 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 = {
@@ -576,7 +578,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}")
@@ -786,16 +788,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, str):
+ 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
@@ -886,7 +900,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
@@ -960,6 +976,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:
@@ -968,7 +987,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)""" %
@@ -1086,3 +1105,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..33d1748 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -1,5 +1,4 @@
{
- "category": "",
"charts": [
{
"chart_name": "Profit and Loss",
@@ -8,18 +7,12 @@
],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Accounts\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Profit and Loss\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chart of Accounts\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Journal Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payment Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Trial Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounting Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Payable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Financial Statements\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Multi Currency\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bank Statement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Subscription Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goods and Services Tax (GST India)\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Share Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Cost Center and Budgeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening and Closing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Profitability\", \"col\": 4}}]",
"creation": "2020-03-02 15:41:59.515192",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "accounting",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Accounting",
"links": [
{
@@ -234,6 +227,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 +343,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 +527,17 @@
"type": "Link"
},
{
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "KSA VAT Report",
+ "link_to": "KSA VAT",
+ "link_type": "Report",
+ "onboard": 0,
+ "only_for": "Saudi Arabia",
+ "type": "Link"
+ },
+ {
"hidden": 0,
"is_query_report": 0,
"label": "Financial Statements",
@@ -1136,6 +1158,16 @@
"type": "Link"
},
{
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "KSA VAT Setting",
+ "link_to": "KSA VAT Setting",
+ "link_type": "DocType",
+ "onboard": 0,
+ "only_for": "Saudi Arabia",
+ "type": "Link"
+ },
+ {
"hidden": 0,
"is_query_report": 0,
"label": "Profitability",
@@ -1188,15 +1220,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:52.872470",
+ "modified": "2021-08-27 12:15:52.872471",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
- "onboarding": "Accounts",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
@@ -1249,4 +1278,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..1945992 100644
--- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py
@@ -1,10 +1,9 @@
-# -*- 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.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..91e6f3f 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..dab2998 100644
--- a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py
+++ b/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py
@@ -1,10 +1,9 @@
-# -*- 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.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..94d7915 100644
--- a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py
+++ b/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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.py b/erpnext/agriculture/doctype/crop/crop.py
index ef02613..ed2073c 100644
--- a/erpnext/agriculture/doctype/crop/crop.py
+++ b/erpnext/agriculture/doctype/crop/crop.py
@@ -1,8 +1,6 @@
-# -*- 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 _
diff --git a/erpnext/agriculture/doctype/crop/crop_dashboard.py b/erpnext/agriculture/doctype/crop/crop_dashboard.py
index 8f37735..37cdbb2 100644
--- a/erpnext/agriculture/doctype/crop/crop_dashboard.py
+++ b/erpnext/agriculture/doctype/crop/crop_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'transactions': [
diff --git a/erpnext/agriculture/doctype/crop/test_crop.py b/erpnext/agriculture/doctype/crop/test_crop.py
index b307983..c79a367 100644
--- a/erpnext/agriculture/doctype/crop/test_crop.py
+++ b/erpnext/agriculture/doctype/crop/test_crop.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
test_dependencies = ["Fertilizer"]
diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
index 9000dea..43c5bbd 100644
--- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
@@ -1,8 +1,6 @@
-# -*- 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 ast
diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py
index 763b403..e4765a5 100644
--- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
diff --git a/erpnext/agriculture/doctype/detected_disease/detected_disease.py b/erpnext/agriculture/doctype/detected_disease/detected_disease.py
index 8c90b83..e507add 100644
--- a/erpnext/agriculture/doctype/detected_disease/detected_disease.py
+++ b/erpnext/agriculture/doctype/detected_disease/detected_disease.py
@@ -1,10 +1,9 @@
-# -*- 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.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 affa570..30ab298 100644
--- a/erpnext/agriculture/doctype/disease/disease.py
+++ b/erpnext/agriculture/doctype/disease/disease.py
@@ -1,12 +1,11 @@
-# -*- 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 import _
+
class Disease(Document):
def validate(self):
diff --git a/erpnext/agriculture/doctype/disease/test_disease.py b/erpnext/agriculture/doctype/disease/test_disease.py
index 8086177..6a6f1e7 100644
--- a/erpnext/agriculture/doctype/disease/test_disease.py
+++ b/erpnext/agriculture/doctype/disease/test_disease.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestDisease(unittest.TestCase):
def test_treatment_period(self):
diff --git a/erpnext/agriculture/doctype/fertilizer/fertilizer.py b/erpnext/agriculture/doctype/fertilizer/fertilizer.py
index c475f00..2408302 100644
--- a/erpnext/agriculture/doctype/fertilizer/fertilizer.py
+++ b/erpnext/agriculture/doctype/fertilizer/fertilizer.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class Fertilizer(Document):
@frappe.whitelist()
def load_contents(self):
diff --git a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py
index 4c71d33..c8630ef 100644
--- a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py
+++ b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestFertilizer(unittest.TestCase):
def test_fertilizer_creation(self):
diff --git a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py b/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py
index d385242..967c3e0 100644
--- a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py
+++ b/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py
@@ -1,10 +1,9 @@
-# -*- 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.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..e1257f3 100644
--- a/erpnext/agriculture/doctype/linked_location/linked_location.py
+++ b/erpnext/agriculture/doctype/linked_location/linked_location.py
@@ -1,10 +1,9 @@
-# -*- 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 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..0bc04af 100644
--- a/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py
+++ b/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py
@@ -1,10 +1,9 @@
-# -*- 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.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..0d29055 100644
--- a/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py
+++ b/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py
@@ -1,10 +1,9 @@
-# -*- 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.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..1438853 100644
--- a/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py
+++ b/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py
@@ -1,10 +1,9 @@
-# -*- 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.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 b65f93d..9a939cd 100644
--- a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py
+++ b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py
@@ -1,12 +1,11 @@
-# -*- 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.model.naming import make_autoname
from frappe.model.document import Document
+
class PlantAnalysis(Document):
@frappe.whitelist()
def load_contents(self):
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..cee241f 100644
--- a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py
+++ b/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..7e6571c 100644
--- a/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py
@@ -1,10 +1,9 @@
-# -*- 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.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 234d0d4..03667fb 100644
--- a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py
+++ b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class SoilAnalysis(Document):
@frappe.whitelist()
def load_contents(self):
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..bb99363 100644
--- a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py
+++ b/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..f501820 100644
--- a/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py
@@ -1,10 +1,9 @@
-# -*- 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.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..b1fc9a0 100644
--- a/erpnext/agriculture/doctype/soil_texture/soil_texture.py
+++ b/erpnext/agriculture/doctype/soil_texture/soil_texture.py
@@ -1,13 +1,12 @@
-# -*- 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.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 16d105c..4549767 100644
--- a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py
+++ b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestSoilTexture(unittest.TestCase):
def test_texture_selection(self):
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..92a0cf9 100644
--- a/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py
+++ b/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py
@@ -1,10 +1,9 @@
-# -*- 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.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..ae144cc 100644
--- a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py
+++ b/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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 cb2691d..434acec 100644
--- a/erpnext/agriculture/doctype/water_analysis/water_analysis.py
+++ b/erpnext/agriculture/doctype/water_analysis/water_analysis.py
@@ -1,12 +1,11 @@
-# -*- 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 import _
+
class WaterAnalysis(Document):
@frappe.whitelist()
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..225c4f6 100644
--- a/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py
@@ -1,10 +1,9 @@
-# -*- 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.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..345baa9 100644
--- a/erpnext/agriculture/doctype/weather/test_weather.py
+++ b/erpnext/agriculture/doctype/weather/test_weather.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..8750709 100644
--- a/erpnext/agriculture/doctype/weather/weather.py
+++ b/erpnext/agriculture/doctype/weather/weather.py
@@ -1,11 +1,11 @@
-# -*- 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.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..7f02ab3 100644
--- a/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py
+++ b/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class WeatherParameter(Document):
pass
diff --git a/erpnext/agriculture/setup.py b/erpnext/agriculture/setup.py
index 75f07be..70931b9 100644
--- a/erpnext/agriculture/setup.py
+++ b/erpnext/agriculture/setup.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
import frappe
from frappe import _
from erpnext.setup.utils import insert_record
diff --git a/erpnext/agriculture/workspace/agriculture/agriculture.json b/erpnext/agriculture/workspace/agriculture/agriculture.json
index 633777e..6714de6 100644
--- a/erpnext/agriculture/workspace/agriculture/agriculture.json
+++ b/erpnext/agriculture/workspace/agriculture/agriculture.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Crops & Lands\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Analytics\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Diseases & Fertilizers\", \"col\": 4}}]",
"creation": "2020-03-02 17:23:34.339274",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "agriculture",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Agriculture",
"links": [
{
@@ -163,15 +156,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:54.595197",
+ "modified": "2021-08-05 12:15:54.595198",
"modified_by": "Administrator",
"module": "Agriculture",
"name": "Agriculture",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "Agriculture",
"roles": [],
diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py
index 2c70179..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())
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index da5778e..c2b1bbc 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -80,20 +80,20 @@
if (frm.doc.docstatus==1) {
if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
- frm.add_custom_button("Transfer Asset", function() {
+ frm.add_custom_button(__("Transfer Asset"), function() {
erpnext.asset.transfer_asset(frm);
}, __("Manage"));
- frm.add_custom_button("Scrap Asset", function() {
+ frm.add_custom_button(__("Scrap Asset"), function() {
erpnext.asset.scrap_asset(frm);
}, __("Manage"));
- frm.add_custom_button("Sell Asset", function() {
+ frm.add_custom_button(__("Sell Asset"), function() {
frm.trigger("make_sales_invoice");
}, __("Manage"));
} else if (frm.doc.status=='Scrapped') {
- frm.add_custom_button("Restore Asset", function() {
+ frm.add_custom_button(__("Restore Asset"), function() {
erpnext.asset.restore_asset(frm);
}, __("Manage"));
}
@@ -121,7 +121,7 @@
}
if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) {
- frm.add_custom_button("View General Ledger", function() {
+ frm.add_custom_button(__("View General Ledger"), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.available_for_use_date,
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 7afb43b..333906a 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -1,20 +1,36 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
# 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 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
+from frappe.utils import (
+ add_days,
+ add_months,
+ cint,
+ date_diff,
+ flt,
+ get_datetime,
+ get_last_day,
+ getdate,
+ month_diff,
+ nowdate,
+ today,
+)
+
+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()
@@ -56,12 +72,12 @@
if self.is_existing_asset and self.purchase_invoice:
frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
- def prepare_depreciation_data(self, date_of_sale=None):
+ def prepare_depreciation_data(self, date_of_sale=None, date_of_return=None):
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
self.make_depreciation_schedule(date_of_sale)
- self.set_accumulated_depreciation(date_of_sale)
+ self.set_accumulated_depreciation(date_of_sale, date_of_return)
else:
self.finance_books = []
self.value_after_depreciation = (flt(self.gross_purchase_amount) -
@@ -121,11 +137,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"))
@@ -168,7 +179,7 @@
d.precision("rate_of_depreciation"))
def make_depreciation_schedule(self, date_of_sale):
- if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules:
+ if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.get('schedules'):
self.schedules = []
if not self.available_for_use_date:
@@ -180,9 +191,8 @@
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))
+ if self.docstatus == 1 and d.value_after_depreciation:
+ value_after_depreciation = flt(d.value_after_depreciation)
else:
value_after_depreciation = (flt(self.gross_purchase_amount) -
flt(self.opening_accumulated_depreciation))
@@ -218,17 +228,19 @@
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
from_date, date_of_sale)
- self.append("schedules", {
- "schedule_date": date_of_sale,
- "depreciation_amount": depreciation_amount,
- "depreciation_method": d.depreciation_method,
- "finance_book": d.finance_book,
- "finance_book_id": d.idx
- })
+ if depreciation_amount > 0:
+ self.append("schedules", {
+ "schedule_date": date_of_sale,
+ "depreciation_amount": depreciation_amount,
+ "depreciation_method": d.depreciation_method,
+ "finance_book": d.finance_book,
+ "finance_book_id": d.idx
+ })
+
break
# For first row
- if has_pro_rata and n==0:
+ if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
self.available_for_use_date, d.depreciation_start_date)
@@ -241,13 +253,17 @@
if not self.flags.increase_in_asset_life:
# In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission
self.to_date = add_months(self.available_for_use_date,
- n * cint(d.frequency_of_depreciation))
+ (n + self.number_of_depreciations_booked) * cint(d.frequency_of_depreciation))
+
+ depreciation_amount_without_pro_rata = depreciation_amount
depreciation_amount, days, months = self.get_pro_rata_amt(d,
depreciation_amount, schedule_date, self.to_date)
- monthly_schedule_date = add_months(schedule_date, 1)
+ depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
+ depreciation_amount, d.finance_book)
+ monthly_schedule_date = add_months(schedule_date, 1)
schedule_date = add_days(schedule_date, days)
last_schedule_date = schedule_date
@@ -337,7 +353,12 @@
# if it returns True, depreciation_amount will not be equal for the first and last rows
def check_is_pro_rata(self, row):
has_pro_rata = False
- days = date_diff(row.depreciation_start_date, self.available_for_use_date) + 1
+
+ # if not existing asset, from_date = available_for_use_date
+ # otherwise, if number_of_depreciations_booked = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
+ # from_date = 01/01/2022
+ from_date = self.get_modified_available_for_use_date(row)
+ days = date_diff(row.depreciation_start_date, from_date) + 1
# if frequency_of_depreciation is 12 months, total_days = 365
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
@@ -347,6 +368,9 @@
return has_pro_rata
+ def get_modified_available_for_use_date(self, row):
+ return add_months(self.available_for_use_date, (self.number_of_depreciations_booked * row.frequency_of_depreciation))
+
def validate_asset_finance_books(self, row):
if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount):
frappe.throw(_("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount")
@@ -375,10 +399,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))
@@ -387,7 +407,29 @@
frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date")
.format(row.idx))
- def set_accumulated_depreciation(self, date_of_sale=None, ignore_booked_entry = False):
+ # to ensure that final accumulated depreciation amount is accurate
+ def get_adjusted_depreciation_amount(self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row, finance_book):
+ if not self.opening_accumulated_depreciation:
+ depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row(finance_book)
+
+ if depreciation_amount_for_first_row + depreciation_amount_for_last_row != depreciation_amount_without_pro_rata:
+ depreciation_amount_for_last_row = depreciation_amount_without_pro_rata - depreciation_amount_for_first_row
+
+ return depreciation_amount_for_last_row
+
+ def get_depreciation_amount_for_first_row(self, finance_book):
+ if self.has_only_one_finance_book():
+ return self.schedules[0].depreciation_amount
+ else:
+ for schedule in self.schedules:
+ if schedule.finance_book == finance_book:
+ return schedule.depreciation_amount
+
+ def has_only_one_finance_book(self):
+ if len(self.finance_books) == 1:
+ return True
+
+ def set_accumulated_depreciation(self, date_of_sale=None, date_of_return=None, ignore_booked_entry = False):
straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line']
finance_books = []
@@ -404,7 +446,7 @@
value_after_depreciation -= flt(depreciation_amount)
# for the last row, if depreciation method = Straight Line
- if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale:
+ if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale and not date_of_return:
book = self.get('finance_books')[cint(d.finance_book_id) - 1]
depreciation_amount += flt(value_after_depreciation -
flt(book.expected_value_after_useful_life), d.precision("depreciation_amount"))
@@ -425,9 +467,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 +589,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
@@ -589,7 +632,7 @@
@frappe.whitelist()
def get_depreciation_rate(self, args, on_validate=False):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
float_precision = cint(frappe.db.get_default("float_precision")) or 2
@@ -783,9 +826,8 @@
@frappe.whitelist()
def make_asset_movement(assets, purpose=None):
import json
- from six import string_types
- if isinstance(assets, string_types):
+ if isinstance(assets, str):
assets = json.loads(assets)
if len(assets) == 0:
@@ -816,13 +858,11 @@
@erpnext.allow_regional
def get_depreciation_amount(asset, depreciable_value, row):
- depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
-
if row.depreciation_method in ("Straight Line", "Manual"):
# if the Depreciation Schedule is being prepared for the first time
if not asset.flags.increase_in_asset_life:
- depreciation_amount = (flt(row.value_after_depreciation) -
- flt(row.expected_value_after_useful_life)) / depreciation_left
+ depreciation_amount = (flt(asset.gross_purchase_amount) -
+ flt(row.expected_value_after_useful_life)) / flt(row.total_number_of_depreciations)
# if the Depreciation Schedule is being modified after Asset Repair
else:
diff --git a/erpnext/assets/doctype/asset/asset_dashboard.py b/erpnext/assets/doctype/asset/asset_dashboard.py
index 62bb4be..00d0847 100644
--- a/erpnext/assets/doctype/asset/asset_dashboard.py
+++ b/erpnext/assets/doctype/asset/asset_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'non_standard_fieldnames': {
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 8fdbbf9..874fb63 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -1,12 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2016, 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, 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
@@ -54,16 +57,18 @@
je.finance_book = d.finance_book
je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, d.depreciation_amount)
+ credit_account, debit_account = get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account)
+
credit_entry = {
- "account": accumulated_depreciation_account,
+ "account": credit_account,
"credit_in_account_currency": d.depreciation_amount,
"reference_type": "Asset",
"reference_name": asset.name,
- "cost_center": ""
+ "cost_center": depreciation_cost_center
}
debit_entry = {
- "account": depreciation_expense_account,
+ "account": debit_account,
"debit_in_account_currency": d.depreciation_amount,
"reference_type": "Asset",
"reference_name": asset.name,
@@ -129,6 +134,20 @@
return fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account
+def get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account):
+ root_type = frappe.get_value("Account", depreciation_expense_account, "root_type")
+
+ if root_type == "Expense":
+ credit_account = accumulated_depreciation_account
+ debit_account = depreciation_expense_account
+ elif root_type == "Income":
+ credit_account = depreciation_expense_account
+ debit_account = accumulated_depreciation_account
+ else:
+ frappe.throw(_("Depreciation Expense Account should be an Income or Expense Account."))
+
+ return credit_account, debit_account
+
@frappe.whitelist()
def scrap_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name)
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 605ce2e..ce2cb01 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -1,23 +1,90 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
-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 frappe.utils import add_days, add_months, cstr, flt, get_last_day, getdate, nowdate
-class TestAsset(unittest.TestCase):
- def setUp(self):
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_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 AssetSetup(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
set_depreciation_settings_in_company()
create_asset_data()
+ enable_cwip_accounting("Computers")
+ make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location")
frappe.db.sql("delete from `tabTax Rule`")
+ @classmethod
+ def tearDownClass(cls):
+ frappe.db.rollback()
+
+class TestAsset(AssetSetup):
+ def test_asset_category_is_fetched(self):
+ """Tests if the Item's Asset Category value is assigned to the Asset, if the field is empty."""
+
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.asset_category = None
+ asset.save()
+
+ self.assertEqual(asset.asset_category, "Computers")
+
+ def test_gross_purchase_amount_is_mandatory(self):
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.gross_purchase_amount = 0
+
+ self.assertRaises(frappe.MandatoryError, asset.save)
+
+ def test_pr_or_pi_mandatory_if_not_existing_asset(self):
+ """Tests if either PI or PR is present if CWIP is enabled and is_existing_asset=0."""
+
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.is_existing_asset=0
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_available_for_use_date_is_after_purchase_date(self):
+ asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, do_not_save=1)
+ asset.is_existing_asset = 0
+ asset.purchase_date = getdate("2021-10-10")
+ asset.available_for_use_date = getdate("2021-10-1")
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_item_exists(self):
+ asset = create_asset(item_code="MacBook", do_not_save=1)
+
+ self.assertRaises(frappe.DoesNotExistError, asset.save)
+
+ def test_validate_item(self):
+ asset = create_asset(item_code="MacBook Pro", do_not_save=1)
+ item = frappe.get_doc("Item", "MacBook Pro")
+
+ item.disabled = 1
+ item.save()
+ self.assertRaises(frappe.ValidationError, asset.save)
+ item.disabled = 0
+
+ item.is_fixed_asset = 0
+ self.assertRaises(frappe.ValidationError, asset.save)
+ item.is_fixed_asset = 1
+
+ item.is_stock_item = 1
+ self.assertRaises(frappe.ValidationError, asset.save)
+
def test_purchase_asset(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=100000.0, location="Test Location")
@@ -80,302 +147,16 @@
doc.set_missing_values()
self.assertEqual(doc.items[0].is_fixed_asset, 1)
- def test_schedule_for_straight_line_method(self):
- 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 = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
-
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save()
-
- self.assertEqual(asset.status, "Draft")
- expected_schedules = [
- ["2030-12-31", 30000.00, 30000.00],
- ["2031-12-31", 30000.00, 60000.00],
- ["2032-12-31", 30000.00, 90000.00]
- ]
-
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_straight_line_method_for_existing_asset(self):
- create_asset(is_existing_asset=1)
- asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
- asset.calculate_depreciation = 1
- asset.number_of_depreciations_booked = 1
- asset.opening_accumulated_depreciation = 40000
- asset.available_for_use_date = "2030-06-06"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- self.assertEqual(asset.status, "Draft")
- asset.save()
- expected_schedules = [
- ["2030-12-31", 14246.58, 54246.58],
- ["2031-12-31", 25000.00, 79246.58],
- ["2032-06-06", 10753.42, 90000.00]
- ]
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_double_declining_method(self):
- 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 = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Double Declining Balance",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": '2030-12-31'
- })
- asset.save()
- self.assertEqual(asset.status, "Draft")
-
- expected_schedules = [
- ['2030-12-31', 66667.00, 66667.00],
- ['2031-12-31', 22222.11, 88889.11],
- ['2032-12-31', 1110.89, 90000.0]
- ]
-
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_double_declining_method_for_existing_asset(self):
- create_asset(is_existing_asset = 1)
- asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
- asset.calculate_depreciation = 1
- asset.is_existing_asset = 1
- asset.number_of_depreciations_booked = 1
- asset.opening_accumulated_depreciation = 50000
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2029-11-30'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Double Declining Balance",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save()
- self.assertEqual(asset.status, "Draft")
-
- expected_schedules = [
- ["2030-12-31", 33333.50, 83333.50],
- ["2031-12-31", 6666.50, 90000.0]
- ]
-
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_prorated_straight_line_method(self):
- 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 = 1
- asset.purchase_date = '2030-01-30'
- asset.is_existing_asset = 0
- asset.available_for_use_date = "2030-01-30"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
-
- asset.save()
-
- expected_schedules = [
- ["2030-12-31", 27534.25, 27534.25],
- ["2031-12-31", 30000.0, 57534.25],
- ["2032-12-31", 30000.0, 87534.25],
- ["2033-01-30", 2465.75, 90000.0]
- ]
-
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_depreciation(self):
- 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 = 1
- asset.purchase_date = '2020-01-30'
- asset.available_for_use_date = "2020-01-30"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.submit()
- asset.load_from_db()
- self.assertEqual(asset.status, "Submitted")
-
- frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-")
- post_depreciation_entries(date="2021-01-01")
- asset.load_from_db()
-
- # check depreciation entry series
- self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
-
- expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
- ("_Test Depreciations - _TC", 30000.0, 0.0)
- )
-
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where against_voucher_type='Asset' and against_voucher = %s
- order by account""", asset.name)
-
- self.assertEqual(gle, expected_gle)
- self.assertEqual(asset.get("value_after_depreciation"), 0)
-
- def test_depreciation_entry_for_wdv_without_pro_rata(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=8000.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 = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
-
- self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
-
- expected_schedules = [
- ["2030-12-31", 4000.00, 4000.00],
- ["2031-12-31", 2000.00, 6000.00],
- ["2032-12-31", 1000.00, 7000.0],
- ]
-
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_pro_rata_depreciation_entry_for_wdv(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=8000.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 = 1
- asset.available_for_use_date = '2030-06-06'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
-
- self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
-
- expected_schedules = [
- ["2030-12-31", 2279.45, 2279.45],
- ["2031-12-31", 2860.28, 5139.73],
- ["2032-12-31", 1430.14, 6569.87],
- ["2033-06-06", 430.13, 7000.0],
- ]
-
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_depreciation_entry_cancellation(self):
- 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 = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.submit()
- post_depreciation_entries(date="2021-01-01")
-
- asset.load_from_db()
-
- # cancel depreciation entry
- depr_entry = asset.get("schedules")[0].journal_entry
- self.assertTrue(depr_entry)
- frappe.get_doc("Journal Entry", depr_entry).cancel()
-
- asset.load_from_db()
- depr_entry = asset.get("schedules")[0].journal_entry
- self.assertFalse(depr_entry)
-
def test_scrap_asset(self):
- 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 = 1
- asset.available_for_use_date = '2020-01-01'
- asset.purchase_date = '2020-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 10,
- "frequency_of_depreciation": 1
- })
- asset.submit()
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = '2020-01-01',
+ purchase_date = '2020-01-01',
+ expected_value_after_useful_life = 10000,
+ total_number_of_depreciations = 10,
+ frequency_of_depreciation = 1,
+ submit = 1
+ )
post_depreciation_entries(date=add_months('2020-01-01', 4))
@@ -402,23 +183,18 @@
self.assertFalse(asset.journal_entry_for_scrap)
self.assertEqual(asset.status, "Partially Depreciated")
- def test_asset_sale(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ def test_gle_made_by_asset_sale(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = '2020-06-06',
+ purchase_date = '2020-01-01',
+ expected_value_after_useful_life = 10000,
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 10,
+ depreciation_start_date = '2020-12-31',
+ submit = 1
+ )
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.submit()
post_depreciation_entries(date="2021-01-01")
si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
@@ -446,30 +222,14 @@
si.cancel()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
- def test_asset_expected_value_after_useful_life(self):
+ def test_expense_head(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ qty=2, rate=200000.0, location="Test Location")
+ doc = make_invoice(pr.name)
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10
- })
- asset.save()
- accumulated_depreciation_after_full_schedule = \
- max(d.accumulated_depreciation_amount for d in asset.get("schedules"))
+ self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
- asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
- flt(accumulated_depreciation_after_full_schedule))
-
- self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
-
+ # CWIP: Capital Work In Progress
def test_cwip_accounting(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=5000, do_not_submit=True, location="Test Location")
@@ -552,14 +312,6 @@
self.assertEqual(gle, expected_gle)
- def test_expense_head(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=2, rate=200000.0, location="Test Location")
-
- doc = make_invoice(pr.name)
-
- self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
-
def test_asset_cwip_toggling_cases(self):
cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
name = frappe.db.get_value("Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"])
@@ -628,35 +380,210 @@
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
+class TestDepreciationMethods(AssetSetup):
+ def test_schedule_for_straight_line_method(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ purchase_date = "2030-01-01",
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+ expected_schedules = [
+ ["2030-12-31", 30000.00, 30000.00],
+ ["2031-12-31", 30000.00, 60000.00],
+ ["2032-12-31", 30000.00, 90000.00]
+ ]
+
+ schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_straight_line_method_for_existing_asset(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-06-06",
+ is_existing_asset = 1,
+ number_of_depreciations_booked = 2,
+ opening_accumulated_depreciation = 47095.89,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2032-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+ expected_schedules = [
+ ["2032-12-31", 30000.0, 77095.89],
+ ["2033-06-06", 12904.11, 90000.0]
+ ]
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_double_declining_method(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ purchase_date = "2030-01-01",
+ depreciation_method = "Double Declining Balance",
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+
+ expected_schedules = [
+ ['2030-12-31', 66667.00, 66667.00],
+ ['2031-12-31', 22222.11, 88889.11],
+ ['2032-12-31', 1110.89, 90000.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_double_declining_method_for_existing_asset(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ is_existing_asset = 1,
+ depreciation_method = "Double Declining Balance",
+ number_of_depreciations_booked = 1,
+ opening_accumulated_depreciation = 50000,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+
+ expected_schedules = [
+ ["2030-12-31", 33333.50, 83333.50],
+ ["2031-12-31", 6666.50, 90000.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_prorated_straight_line_method(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-30",
+ purchase_date = "2030-01-30",
+ depreciation_method = "Straight Line",
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ expected_schedules = [
+ ["2030-12-31", 27534.25, 27534.25],
+ ["2031-12-31", 30000.0, 57534.25],
+ ["2032-12-31", 30000.0, 87534.25],
+ ["2033-01-30", 2465.75, 90000.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ # WDV: Written Down Value method
+ def test_depreciation_entry_for_wdv_without_pro_rata(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ purchase_date = "2030-01-01",
+ depreciation_method = "Written Down Value",
+ expected_value_after_useful_life = 12500,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+
+ expected_schedules = [
+ ["2030-12-31", 50000.0, 50000.0],
+ ["2031-12-31", 25000.0, 75000.0],
+ ["2032-12-31", 12500.0, 87500.0],
+ ]
+
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ # WDV: Written Down Value method
+ def test_pro_rata_depreciation_entry_for_wdv(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-06-06",
+ purchase_date = "2030-01-01",
+ depreciation_method = "Written Down Value",
+ expected_value_after_useful_life = 12500,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+
+ expected_schedules = [
+ ["2030-12-31", 28493.15, 28493.15],
+ ["2031-12-31", 35753.43, 64246.58],
+ ["2032-12-31", 17876.71, 82123.29],
+ ["2033-06-06", 5376.71, 87500.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
def test_discounted_wdv_depreciation_rate_for_indian_region(self):
# set indian company
company_flag = frappe.flags.company
frappe.flags.company = "_Test Company"
- 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 = True)
- 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", {
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-07-12",
+ purchase_date = "2030-01-01",
+ finance_book = finance_book.name,
+ depreciation_method = "Written Down Value",
+ expected_value_after_useful_life = 12500,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [
- ["2030-12-31", 942.47, 942.47],
- ["2031-12-31", 3528.77, 4471.24],
- ["2032-12-31", 1764.38, 6235.62],
- ["2033-07-12", 764.38, 7000.00]
+ ["2030-12-31", 11780.82, 11780.82],
+ ["2031-12-31", 44109.59, 55890.41],
+ ["2032-12-31", 22054.8, 77945.21],
+ ["2033-07-12", 9554.79, 87500.0]
]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@@ -667,6 +594,466 @@
# reset indian company
frappe.flags.company = company_flag
+class TestDepreciationBasics(AssetSetup):
+ def test_depreciation_without_pro_rata(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = getdate("2019-12-31"),
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = getdate("2020-12-31"),
+ submit = 1
+ )
+
+ expected_values = [
+ ["2020-12-31", 30000, 30000],
+ ["2021-12-31", 30000, 60000],
+ ["2022-12-31", 30000, 90000]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+
+ def test_depreciation_with_pro_rata(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = getdate("2019-12-31"),
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = getdate("2020-07-01"),
+ submit = 1
+ )
+
+ expected_values = [
+ ["2020-07-01", 15000, 15000],
+ ["2021-07-01", 30000, 45000],
+ ["2022-07-01", 30000, 75000],
+ ["2022-12-31", 15000, 90000]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+
+ def test_get_depreciation_amount(self):
+ """Tests if get_depreciation_amount() returns the right value."""
+
+ from erpnext.assets.doctype.asset.asset import get_depreciation_amount
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ available_for_use_date = "2019-12-31"
+ )
+
+ asset.calculate_depreciation = 1
+ asset.append("finance_books", {
+ "depreciation_method": "Straight Line",
+ "frequency_of_depreciation": 12,
+ "total_number_of_depreciations": 3,
+ "expected_value_after_useful_life": 10000,
+ "depreciation_start_date": "2020-12-31"
+ })
+
+ depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0])
+ self.assertEqual(depreciation_amount, 30000)
+
+ def test_make_depreciation_schedule(self):
+ """Tests if make_depreciation_schedule() returns the right values."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_method = "Straight Line",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-12-31"
+ )
+
+ expected_values = [
+ ['2020-12-31', 30000.0],
+ ['2021-12-31', 30000.0],
+ ['2022-12-31', 30000.0]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(expected_values[i][0], schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+
+ def test_set_accumulated_depreciation(self):
+ """Tests if set_accumulated_depreciation() returns the right values."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_method = "Straight Line",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-12-31"
+ )
+
+ expected_values = [30000.0, 60000.0, 90000.0]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(expected_values[i], schedule.accumulated_depreciation_amount)
+
+ def test_check_is_pro_rata(self):
+ """Tests if check_is_pro_rata() returns the right value(i.e. checks if has_pro_rata is accurate)."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ available_for_use_date = "2019-12-31",
+ do_not_save = 1
+ )
+
+ asset.calculate_depreciation = 1
+ asset.append("finance_books", {
+ "depreciation_method": "Straight Line",
+ "frequency_of_depreciation": 12,
+ "total_number_of_depreciations": 3,
+ "expected_value_after_useful_life": 10000,
+ "depreciation_start_date": "2020-12-31"
+ })
+
+ has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0])
+ self.assertFalse(has_pro_rata)
+
+ asset.finance_books = []
+ asset.append("finance_books", {
+ "depreciation_method": "Straight Line",
+ "frequency_of_depreciation": 12,
+ "total_number_of_depreciations": 3,
+ "expected_value_after_useful_life": 10000,
+ "depreciation_start_date": "2020-07-01"
+ })
+
+ has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0])
+ self.assertTrue(has_pro_rata)
+
+ def test_expected_value_after_useful_life_greater_than_purchase_amount(self):
+ """Tests if an error is raised when expected_value_after_useful_life(110,000) > gross_purchase_amount(100,000)."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 110000,
+ depreciation_start_date = "2020-07-01",
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_depreciation_start_date(self):
+ """Tests if an error is raised when neither depreciation_start_date nor available_for_use_date are specified."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 110000,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_opening_accumulated_depreciation(self):
+ """Tests if an error is raised when opening_accumulated_depreciation > (gross_purchase_amount - expected_value_after_useful_life)."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-07-01",
+ opening_accumulated_depreciation = 100000,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_number_of_depreciations_booked(self):
+ """Tests if an error is raised when number_of_depreciations_booked is not specified when opening_accumulated_depreciation is."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-07-01",
+ opening_accumulated_depreciation = 10000,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_number_of_depreciations(self):
+ """Tests if an error is raised when number_of_depreciations_booked > total_number_of_depreciations."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-07-01",
+ opening_accumulated_depreciation = 10000,
+ number_of_depreciations_booked = 5,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_depreciation_start_date_is_before_purchase_date(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2014-07-01",
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_depreciation_start_date_is_before_available_for_use_date(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2018-07-01",
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_finance_books_are_present_if_calculate_depreciation_is_enabled(self):
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.calculate_depreciation = 1
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_post_depreciation_entries(self):
+ """Tests if post_depreciation_entries() works as expected."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-06-01")
+ asset.load_from_db()
+
+ self.assertTrue(asset.schedules[0].journal_entry)
+ self.assertFalse(asset.schedules[1].journal_entry)
+ self.assertFalse(asset.schedules[2].journal_entry)
+
+ def test_depr_entry_posting_when_depr_expense_account_is_an_expense_account(self):
+ """Tests if the Depreciation Expense Account gets debited and the Accumulated Depreciation Account gets credited when the former's an Expense Account."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-06-01")
+ asset.load_from_db()
+
+ je = frappe.get_doc("Journal Entry", asset.schedules[0].journal_entry)
+ accounting_entries = [{"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts]
+
+ for entry in accounting_entries:
+ if entry["account"] == "_Test Depreciations - _TC":
+ self.assertTrue(entry["debit"])
+ self.assertFalse(entry["credit"])
+ else:
+ self.assertTrue(entry["credit"])
+ self.assertFalse(entry["debit"])
+
+ def test_depr_entry_posting_when_depr_expense_account_is_an_income_account(self):
+ """Tests if the Depreciation Expense Account gets credited and the Accumulated Depreciation Account gets debited when the former's an Income Account."""
+
+ depr_expense_account = frappe.get_doc("Account", "_Test Depreciations - _TC")
+ depr_expense_account.root_type = "Income"
+ depr_expense_account.parent_account = "Income - _TC"
+ depr_expense_account.save()
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-06-01")
+ asset.load_from_db()
+
+ je = frappe.get_doc("Journal Entry", asset.schedules[0].journal_entry)
+ accounting_entries = [{"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts]
+
+ for entry in accounting_entries:
+ if entry["account"] == "_Test Depreciations - _TC":
+ self.assertTrue(entry["credit"])
+ self.assertFalse(entry["debit"])
+ else:
+ self.assertTrue(entry["debit"])
+ self.assertFalse(entry["credit"])
+
+ # resetting
+ depr_expense_account.root_type = "Expense"
+ depr_expense_account.parent_account = "Expenses - _TC"
+ depr_expense_account.save()
+
+ def test_clear_depreciation_schedule(self):
+ """Tests if clear_depreciation_schedule() works as expected."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-06-01")
+ asset.load_from_db()
+
+ asset.clear_depreciation_schedule()
+
+ self.assertEqual(len(asset.schedules), 1)
+
+ def test_depreciation_entry_cancellation(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ purchase_date = "2020-06-06",
+ available_for_use_date = "2020-06-06",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 10,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-01-01")
+
+ asset.load_from_db()
+
+ # cancel depreciation entry
+ depr_entry = asset.get("schedules")[0].journal_entry
+ self.assertTrue(depr_entry)
+ frappe.get_doc("Journal Entry", depr_entry).cancel()
+
+ asset.load_from_db()
+ depr_entry = asset.get("schedules")[0].journal_entry
+ self.assertFalse(depr_entry)
+
+ def test_asset_expected_value_after_useful_life(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2020-06-06",
+ purchase_date = "2020-06-06",
+ frequency_of_depreciation = 10,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000
+ )
+
+ accumulated_depreciation_after_full_schedule = \
+ max(d.accumulated_depreciation_amount for d in asset.get("schedules"))
+
+ asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
+ flt(accumulated_depreciation_after_full_schedule))
+
+ self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
+
+ def test_gle_made_by_depreciation_entries(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ purchase_date = "2020-01-30",
+ available_for_use_date = "2020-01-30",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 10,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ self.assertEqual(asset.status, "Submitted")
+
+ frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-")
+ post_depreciation_entries(date="2021-01-01")
+ asset.load_from_db()
+
+ # check depreciation entry series
+ self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
+
+ expected_gle = (
+ ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
+ ("_Test Depreciations - _TC", 30000.0, 0.0)
+ )
+
+ gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ where against_voucher_type='Asset' and against_voucher = %s
+ order by account""", asset.name)
+
+ self.assertEqual(gle, expected_gle)
+ self.assertEqual(asset.get("value_after_depreciation"), 0)
+ def test_expected_value_change(self):
+ """
+ tests if changing `expected_value_after_useful_life`
+ affects `value_after_depreciation`
+ """
+
+ asset = create_asset(calculate_depreciation=1)
+ asset.opening_accumulated_depreciation = 2000
+ asset.number_of_depreciations_booked = 1
+
+ asset.finance_books[0].expected_value_after_useful_life = 100
+ asset.save()
+ asset.reload()
+ self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
+
+ # changing expected_value_after_useful_life shouldn't affect value_after_depreciation
+ asset.finance_books[0].expected_value_after_useful_life = 200
+ asset.save()
+ asset.reload()
+ self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
+
def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"):
create_asset_category()
@@ -688,32 +1075,37 @@
asset = frappe.get_doc({
"doctype": "Asset",
"asset_name": args.asset_name or "Macbook Pro 1",
- "asset_category": "Computers",
+ "asset_category": args.asset_category or "Computers",
"item_code": args.item_code or "Macbook Pro",
- "company": args.company or"_Test Company",
- "purchase_date": "2015-01-01",
+ "company": args.company or "_Test Company",
+ "purchase_date": args.purchase_date or "2015-01-01",
"calculate_depreciation": args.calculate_depreciation or 0,
- "gross_purchase_amount": 100000,
- "purchase_receipt_amount": 100000,
- "expected_value_after_useful_life": 10000,
+ "opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0,
+ "number_of_depreciations_booked": args.number_of_depreciations_booked or 0,
+ "gross_purchase_amount": args.gross_purchase_amount or 100000,
+ "purchase_receipt_amount": args.purchase_receipt_amount or 100000,
"warehouse": args.warehouse or "_Test Warehouse - _TC",
- "available_for_use_date": "2020-06-06",
- "location": "Test Location",
- "asset_owner": "Company",
- "is_existing_asset": 1
+ "available_for_use_date": args.available_for_use_date or "2020-06-06",
+ "location": args.location or "Test Location",
+ "asset_owner": args.asset_owner or "Company",
+ "is_existing_asset": args.is_existing_asset or 1
})
if asset.calculate_depreciation:
asset.append("finance_books", {
- "depreciation_method": "Straight Line",
- "frequency_of_depreciation": 12,
- "total_number_of_depreciations": 5
+ "finance_book": args.finance_book,
+ "depreciation_method": args.depreciation_method or "Straight Line",
+ "frequency_of_depreciation": args.frequency_of_depreciation or 12,
+ "total_number_of_depreciations": args.total_number_of_depreciations or 5,
+ "expected_value_after_useful_life": args.expected_value_after_useful_life or 0,
+ "depreciation_start_date": args.depreciation_start_date
})
- try:
- asset.save()
- except frappe.DuplicateEntryError:
- pass
+ if not args.do_not_save:
+ try:
+ asset.save()
+ except frappe.DuplicateEntryError:
+ pass
if args.submit:
asset.submit()
@@ -764,3 +1156,6 @@
# Enable booking asset depreciation entry automatically
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
+
+def enable_cwip_accounting(asset_category, enable=1):
+ frappe.db.set_value("Asset Category", asset_category, "enable_cwip_accounting", enable)
diff --git a/erpnext/assets/doctype/asset_category/asset_category.js b/erpnext/assets/doctype/asset_category/asset_category.js
index 51ce157..c702687 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.js
+++ b/erpnext/assets/doctype/asset_category/asset_category.js
@@ -33,7 +33,7 @@
var d = locals[cdt][cdn];
return {
"filters": {
- "root_type": "Expense",
+ "root_type": ["in", ["Expense", "Income"]],
"is_group": 0,
"company": d.company_name
}
diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py
index 39032d6..bd573bf 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -1,12 +1,12 @@
-# -*- 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 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):
@@ -42,10 +42,10 @@
def validate_account_types(self):
account_type_map = {
- 'fixed_asset_account': { 'account_type': 'Fixed Asset' },
- 'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' },
- 'depreciation_expense_account': { 'root_type': 'Expense' },
- 'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' }
+ 'fixed_asset_account': {'account_type': ['Fixed Asset']},
+ 'accumulated_depreciation_account': {'account_type': ['Accumulated Depreciation']},
+ 'depreciation_expense_account': {'root_type': ['Expense', 'Income']},
+ 'capital_work_in_progress_account': {'account_type': ['Capital Work in Progress']}
}
for d in self.accounts:
for fieldname in account_type_map.keys():
@@ -53,11 +53,11 @@
selected_account = d.get(fieldname)
key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match)
- expected_key_type = account_type_map[fieldname][key_to_match]
+ expected_key_types = account_type_map[fieldname][key_to_match]
- if selected_key_type != expected_key_type:
+ if selected_key_type not in expected_key_types:
frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
- .format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)),
+ .format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_types)),
title=_("Invalid Account"))
def valide_cwip_account(self):
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 9f7ada6..3d19fa3 100644
--- a/erpnext/assets/doctype/asset_category/test_asset_category.py
+++ b/erpnext/assets/doctype/asset_category/test_asset_category.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestAssetCategory(unittest.TestCase):
def test_mandatory_fields(self):
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..e06d233 100644
--- a/erpnext/assets/doctype/asset_category_account/asset_category_account.py
+++ b/erpnext/assets/doctype/asset_category_account/asset_category_account.py
@@ -1,10 +1,9 @@
-# -*- 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.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..292ca13 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
@@ -1,10 +1,9 @@
-# -*- 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 AssetFinanceBook(Document):
pass
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
index e14f1d8..4fc4c4c 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
@@ -1,14 +1,14 @@
-# -*- 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.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'):
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 7610152..8acb61b 100644
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+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):
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..7d3453f 100644
--- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
+++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
@@ -1,14 +1,15 @@
-# -*- 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.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..9980ff3 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..1078208 100644
--- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py
+++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py
@@ -1,10 +1,9 @@
-# -*- 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.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..938c99b 100644
--- a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py
+++ b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py
@@ -1,10 +1,9 @@
-# -*- 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.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..732ab4a 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestAssetMaintenanceTeam(unittest.TestCase):
pass
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index 1771e27..07bea61 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -1,12 +1,12 @@
-# -*- 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.model.document import Document
+
class AssetMovement(Document):
def validate(self):
self.validate_asset()
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..025facc 100644
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
@@ -1,18 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+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..e25226d 100644
--- a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py
+++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 AssetMovementItem(Document):
pass
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js
index 18a56d3..d554d52 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.js
@@ -60,6 +60,10 @@
if (frm.doc.repair_status == "Completed") {
frm.set_value('completion_date', frappe.datetime.now_datetime());
}
+ },
+
+ stock_items_on_form_rendered() {
+ erpnext.setup_serial_or_batch_no();
}
});
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index 746f582..36848e9 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -1,15 +1,16 @@
-# -*- 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.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)
@@ -117,9 +118,10 @@
for stock_item in self.get('stock_items'):
stock_entry.append('items', {
"s_warehouse": self.warehouse,
- "item_code": stock_item.item,
+ "item_code": stock_item.item_code,
"qty": stock_item.consumed_quantity,
- "basic_rate": stock_item.valuation_rate
+ "basic_rate": stock_item.valuation_rate,
+ "serial_no": stock_item.serial_no
})
stock_entry.insert()
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 5e727d0..7c0d057 100644
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
@@ -1,20 +1,29 @@
-# -*- coding: utf-8 -*-
# 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,
+)
+from erpnext.stock.doctype.item.test_item import create_item
+
class TestAssetRepair(unittest.TestCase):
- def setUp(self):
+ @classmethod
+ def setUpClass(cls):
set_depreciation_settings_in_company()
create_asset_data()
+ create_item("_Test Stock Item")
frappe.db.sql("delete from `tabTax Rule`")
def test_update_status(self):
- asset = create_asset()
+ asset = create_asset(submit=1)
initial_status = asset.status
asset_repair = create_asset_repair(asset = asset)
@@ -64,11 +73,30 @@
self.assertEqual(stock_entry.stock_entry_type, "Material Issue")
self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse)
- self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item)
+ self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item_code)
self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity)
+ def test_serialized_item_consumption(self):
+ from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+ stock_entry = make_serialized_item()
+ serial_nos = stock_entry.get("items")[0].serial_no
+ serial_no = serial_nos.split("\n")[0]
+
+ # should not raise any error
+ create_asset_repair(stock_consumption = 1, item_code = stock_entry.get("items")[0].item_code,
+ warehouse = "_Test Warehouse - _TC", serial_no = serial_no, submit = 1)
+
+ # should raise error
+ asset_repair = create_asset_repair(stock_consumption = 1, warehouse = "_Test Warehouse - _TC",
+ item_code = stock_entry.get("items")[0].item_code)
+
+ asset_repair.repair_status = "Completed"
+ self.assertRaises(SerialNoRequiredError, asset_repair.submit)
+
def test_increase_in_asset_value_due_to_stock_consumption(self):
- asset = create_asset(calculate_depreciation = 1)
+ asset = create_asset(calculate_depreciation = 1, submit=1)
initial_asset_value = get_asset_value(asset)
asset_repair = create_asset_repair(asset= asset, stock_consumption = 1, submit = 1)
asset.reload()
@@ -77,7 +105,7 @@
self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value)
def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self):
- asset = create_asset(calculate_depreciation = 1)
+ asset = create_asset(calculate_depreciation = 1, submit=1)
initial_asset_value = get_asset_value(asset)
asset_repair = create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
asset.reload()
@@ -95,7 +123,7 @@
self.assertEqual(asset_repair.name, gl_entry.voucher_no)
def test_increase_in_asset_life(self):
- asset = create_asset(calculate_depreciation = 1)
+ asset = create_asset(calculate_depreciation = 1, submit=1)
initial_num_of_depreciations = num_of_depreciations(asset)
create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
asset.reload()
@@ -110,15 +138,15 @@
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)
if args.asset:
asset = args.asset
else:
- asset = create_asset(is_existing_asset = 1)
+ asset = create_asset(is_existing_asset = 1, submit=1)
asset_repair = frappe.new_doc("Asset Repair")
asset_repair.update({
"asset": asset.name,
@@ -131,11 +159,12 @@
if args.stock_consumption:
asset_repair.stock_consumption = 1
- asset_repair.warehouse = create_warehouse("Test Warehouse", company = asset.company)
+ asset_repair.warehouse = args.warehouse or create_warehouse("Test Warehouse", company = asset.company)
asset_repair.append("stock_items", {
- "item": args.item or args.item_code or "_Test Item",
+ "item_code": args.item_code or "_Test Stock Item",
"valuation_rate": args.rate if args.get("rate") is not None else 100,
- "consumed_quantity": args.qty or 1
+ "consumed_quantity": args.qty or 1,
+ "serial_no": args.serial_no
})
asset_repair.insert(ignore_if_duplicate=True)
@@ -152,7 +181,7 @@
})
stock_entry.append('items', {
"t_warehouse": asset_repair.warehouse,
- "item_code": asset_repair.stock_items[0].item,
+ "item_code": asset_repair.stock_items[0].item_code,
"qty": asset_repair.stock_items[0].consumed_quantity
})
stock_entry.submit()
diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
index 528f0ec..f63add1 100644
--- a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
+++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
@@ -5,20 +5,14 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "item",
+ "item_code",
"valuation_rate",
"consumed_quantity",
- "total_value"
+ "total_value",
+ "serial_no"
],
"fields": [
{
- "fieldname": "item",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Item",
- "options": "Item"
- },
- {
"fetch_from": "item.valuation_rate",
"fieldname": "valuation_rate",
"fieldtype": "Currency",
@@ -38,12 +32,24 @@
"in_list_view": 1,
"label": "Total Value",
"read_only": 1
+ },
+ {
+ "fieldname": "serial_no",
+ "fieldtype": "Small Text",
+ "label": "Serial No"
+ },
+ {
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item",
+ "options": "Item"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-05-12 03:19:55.006300",
+ "modified": "2021-11-11 18:23:00.492483",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Repair Consumed Item",
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.js b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
index 79c8861..36f510b 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
@@ -14,6 +14,14 @@
}
}
});
+ frm.set_query('asset', function() {
+ return {
+ filters: {
+ calculate_depreciation: 1,
+ docstatus: 1
+ }
+ };
+ });
},
onload: function(frm) {
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..0b646ed 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -1,14 +1,21 @@
-# -*- 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.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.asset import get_depreciation_amount
+from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
+from erpnext.regional.india.utils import (
+ get_depreciation_amount as get_depreciation_amount_for_india,
+)
+
class AssetValueAdjustment(Document):
def validate(self):
@@ -87,6 +94,7 @@
def reschedule_depreciations(self, asset_value):
asset = frappe.get_doc('Asset', self.asset)
+ country = frappe.get_value('Company', self.company, 'country')
for d in asset.finance_books:
d.value_after_depreciation = asset_value
@@ -108,8 +116,10 @@
depreciation_amount = days * rate_per_day
from_date = data.schedule_date
else:
- depreciation_amount = asset.get_depreciation_amount(value_after_depreciation,
- no_of_depreciations, d)
+ if country == "India":
+ depreciation_amount = get_depreciation_amount_for_india(asset, value_after_depreciation, d)
+ else:
+ depreciation_amount = get_depreciation_amount(asset, value_after_depreciation, d)
if depreciation_amount:
value_after_depreciation -= flt(depreciation_amount)
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 a9dc979..ef13c56 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
@@ -1,14 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, get_last_day, add_days
+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):
diff --git a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py
index 54fba3f..b597c58 100644
--- a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py
+++ b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py
@@ -1,10 +1,9 @@
-# -*- 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.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..e1257f3 100644
--- a/erpnext/assets/doctype/linked_location/linked_location.py
+++ b/erpnext/assets/doctype/linked_location/linked_location.py
@@ -1,10 +1,9 @@
-# -*- 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 LinkedLocation(Document):
pass
diff --git a/erpnext/assets/doctype/location/location.py b/erpnext/assets/doctype/location/location.py
index 317894c..abc7325 100644
--- a/erpnext/assets/doctype/location/location.py
+++ b/erpnext/assets/doctype/location/location.py
@@ -1,15 +1,11 @@
-# -*- 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 json
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/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/location/test_location.py b/erpnext/assets/doctype/location/test_location.py
index c98b0b0..36e1dd4 100644
--- a/erpnext/assets/doctype/location/test_location.py
+++ b/erpnext/assets/doctype/location/test_location.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import json
import unittest
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..c3ede94 100644
--- a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py
+++ b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py
@@ -1,10 +1,9 @@
-# -*- 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.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..911a654 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 75f42a9..06989a9 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -16,9 +16,8 @@
fieldname:"status",
label: __("Status"),
fieldtype: "Select",
- options: "In Location\nDisposed",
- default: 'In Location',
- reqd: 1
+ options: "\nIn Location\nDisposed",
+ default: 'In Location'
},
{
"fieldname":"filter_based_on",
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 7d07397..db51336 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -1,11 +1,17 @@
# 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, 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 {})
@@ -38,12 +44,13 @@
if filters.get('cost_center'):
conditions["cost_center"] = filters.get('cost_center')
- # In Store assets are those that are not sold or scrapped
- operand = 'not in'
- if status not in 'In Location':
- operand = 'in'
+ if status:
+ # In Store assets are those that are not sold or scrapped
+ operand = 'not in'
+ if status not in 'In Location':
+ operand = 'in'
- conditions['status'] = (operand, ['Sold', 'Scrapped'])
+ conditions['status'] = (operand, ['Sold', 'Scrapped'])
return conditions
diff --git a/erpnext/assets/workspace/assets/assets.json b/erpnext/assets/workspace/assets/assets.json
index dfbf1a3..495de46 100644
--- a/erpnext/assets/workspace/assets/assets.json
+++ b/erpnext/assets/workspace/assets/assets.json
@@ -1,5 +1,4 @@
{
- "category": "",
"charts": [
{
"chart_name": "Asset Value Analytics",
@@ -8,18 +7,12 @@
],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Assets\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Asset Value Analytics\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset Category\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fixed Asset Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assets\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:43:27.634865",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "assets",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Assets",
"links": [
{
@@ -179,15 +172,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:54.839452",
+ "modified": "2021-08-05 12:15:54.839453",
"modified_by": "Administrator",
"module": "Assets",
"name": "Assets",
- "onboarding": "Assets",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/buying/__init__.py b/erpnext/buying/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/__init__.py
+++ b/erpnext/buying/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/buying/doctype/__init__.py b/erpnext/buying/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/__init__.py
+++ b/erpnext/buying/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js
index 944bb61..32431fc 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.js
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.js
@@ -11,7 +11,7 @@
{
fieldname: "supp_master_name",
title: "Supplier Naming By",
- description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
+ description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a <a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a> choose the 'Naming Series' option."),
},
{
fieldname: "buying_price_list",
diff --git a/erpnext/buying/doctype/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..2b6ff43 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.py
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.py
@@ -3,11 +3,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..cdb691d 100644
--- a/erpnext/buying/doctype/buying_settings/test_buying_settings.py
+++ b/erpnext/buying/doctype/buying_settings/test_buying_settings.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestBuyingSettings(unittest.TestCase):
pass
diff --git a/erpnext/buying/doctype/purchase_order/__init__.py b/erpnext/buying/doctype/purchase_order/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/purchase_order/__init__.py
+++ b/erpnext/buying/doctype/purchase_order/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 a55a0b7..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,35 +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",
- "options": "Barcode",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -577,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",
@@ -615,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",
@@ -639,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",
@@ -652,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",
@@ -679,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",
@@ -707,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",
@@ -737,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",
@@ -756,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",
@@ -778,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",
@@ -791,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",
@@ -805,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",
@@ -824,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",
@@ -837,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",
@@ -848,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",
@@ -867,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",
@@ -877,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",
@@ -918,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",
@@ -930,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.",
@@ -943,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",
@@ -955,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",
@@ -974,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",
@@ -986,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",
@@ -1015,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",
@@ -1026,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",
@@ -1052,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,
@@ -1063,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",
@@ -1074,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",
@@ -1109,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",
@@ -1122,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",
@@ -1134,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",
@@ -1161,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",
@@ -1173,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,
@@ -1185,8 +983,6 @@
"oldfieldtype": "Column Break",
"print_hide": 1,
"print_width": "50%",
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -1197,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,
@@ -1211,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,
@@ -1227,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,
@@ -1253,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,
@@ -1263,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",
@@ -1280,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,
@@ -1318,9 +1088,7 @@
},
{
"fieldname": "column_break_75",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "billing_address",
@@ -1353,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
@@ -1361,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-08-17 20:16:12.737743",
+ "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 ca3bd90..1b5f35e 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -1,24 +1,30 @@
# 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 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"
@@ -66,6 +72,7 @@
self.create_raw_materials_supplied("supplied_items")
self.set_received_qty_for_drop_ship_items()
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_order_reference)
+ self.reset_default_field_value("set_warehouse", "items", "warehouse")
def validate_with_previous_doc(self):
super(PurchaseOrder, self).validate_with_previous_doc({
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
index ab514da..0163595 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': '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 fa174ba..9a63afc 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -1,25 +1,31 @@
# 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 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 +421,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,7 +492,9 @@
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 +987,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_item/__init__.py b/erpnext/buying/doctype/purchase_order_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/purchase_order_item/__init__.py
+++ b/erpnext/buying/doctype/purchase_order_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 b6e28b6..0cef0de 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
@@ -1,11 +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 frappe
from frappe.model.document import Document
+
class PurchaseOrderItem(Document):
pass
diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/__init__.py b/erpnext/buying/doctype/purchase_order_item_supplied/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/purchase_order_item_supplied/__init__.py
+++ b/erpnext/buying/doctype/purchase_order_item_supplied/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 c85ca2f..c69b5ed 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class PurchaseOrderItemSupplied(Document):
pass
diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/__init__.py b/erpnext/buying/doctype/purchase_receipt_item_supplied/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/purchase_receipt_item_supplied/__init__.py
+++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 00c93ed..7b67921 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class PurchaseReceiptItemSupplied(Document):
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 8ed6c9e..2db750e 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -1,22 +1,22 @@
-# -*- 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, 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
-from six import string_types
+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 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")
@@ -287,7 +287,7 @@
# This method is used to make supplier quotation from supplier's portal.
@frappe.whitelist()
def create_supplier_quotation(doc):
- if isinstance(doc, string_types):
+ if isinstance(doc, str):
doc = json.loads(doc)
try:
@@ -391,12 +391,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
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 751336d..dc1cda1 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,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'docstatus': 1,
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..5190199 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
@@ -1,18 +1,20 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
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_item/request_for_quotation_item.py b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.py
index cc897af..096aede 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
@@ -1,10 +1,9 @@
-# -*- 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.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..dbaad47 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
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class RequestforQuotationSupplier(Document):
pass
diff --git a/erpnext/buying/doctype/supplier/__init__.py b/erpnext/buying/doctype/supplier/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/supplier/__init__.py
+++ b/erpnext/buying/doctype/supplier/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 1766c2c..f0899b0 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' }
@@ -64,6 +83,12 @@
frm.trigger("get_supplier_group_details");
}, __('Actions'));
+ if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
+ frm.add_custom_button(__('Link with Customer'), function () {
+ frm.trigger('show_party_link_dialog');
+ }, __('Actions'));
+ }
+
// indicators
erpnext.utils.set_party_dashboard_indicators(frm);
}
@@ -78,6 +103,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);
@@ -85,5 +134,42 @@
else {
frm.toggle_reqd("represents_company", false);
}
+ },
+ show_party_link_dialog: function(frm) {
+ const dialog = new frappe.ui.Dialog({
+ title: __('Select a Customer'),
+ fields: [{
+ fieldtype: 'Link', label: __('Customer'),
+ options: 'Customer', fieldname: 'customer', reqd: 1
+ }],
+ primary_action: function({ customer }) {
+ frappe.call({
+ method: 'erpnext.accounts.doctype.party_link.party_link.create_party_link',
+ args: {
+ primary_role: 'Supplier',
+ primary_party: frm.doc.name,
+ secondary_party: customer
+ },
+ freeze: true,
+ callback: function() {
+ dialog.hide();
+ frappe.msgprint({
+ message: __('Successfully linked to Customer'),
+ alert: true
+ });
+ },
+ error: function() {
+ dialog.hide();
+ frappe.msgprint({
+ message: __('Linking to Customer Failed. Please try again.'),
+ title: __('Linking Failed'),
+ indicator: 'red'
+ });
+ }
+ });
+ },
+ primary_action_label: __('Create Link')
+ });
+ dialog.show();
}
});
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 38b8dfd..a57d9a9 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -25,7 +25,6 @@
"column_break0",
"supplier_group",
"supplier_type",
- "pan",
"allow_purchase_invoice_creation_without_purchase_order",
"allow_purchase_invoice_creation_without_purchase_receipt",
"disabled",
@@ -49,6 +48,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",
@@ -170,11 +176,6 @@
"reqd": 1
},
{
- "fieldname": "pan",
- "fieldtype": "Data",
- "label": "PAN"
- },
- {
"fieldname": "language",
"fieldtype": "Link",
"label": "Print Language",
@@ -378,6 +379,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,16 +427,17 @@
"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-10-20 22:03:33.147249",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
"name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index fd16b23..14d2ccd 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -1,14 +1,22 @@
# 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 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 ( # noqa
+ get_dashboard_info,
+ get_timeline_data,
+ 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 +43,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 +91,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 +152,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/supplier_dashboard.py b/erpnext/buying/doctype/supplier/supplier_dashboard.py
index 1625103..78efd8e 100644
--- a/erpnext/buying/doctype/supplier/supplier_dashboard.py
+++ b/erpnext/buying/doctype/supplier/supplier_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index 8980466..13fe9df 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -1,12 +1,13 @@
# 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, 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/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 4473dde..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."))
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/__init__.py b/erpnext/buying/doctype/supplier_quotation/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/supplier_quotation/__init__.py
+++ b/erpnext/buying/doctype/supplier_quotation/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 0a51a8e..023c95d 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -17,6 +17,7 @@
"company",
"transaction_date",
"valid_till",
+ "quotation_number",
"amended_from",
"address_section",
"supplier_address",
@@ -797,6 +798,11 @@
"fieldtype": "Date",
"in_list_view": 1,
"label": "Valid Till"
+ },
+ {
+ "fieldname": "quotation_number",
+ "fieldtype": "Data",
+ "label": "Quotation Number"
}
],
"icon": "fa fa-shopping-cart",
@@ -804,10 +810,11 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-04-19 00:58:20.995491",
+ "modified": "2021-12-11 06:43:20.924080",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 25e4e2a..d65ab94 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -1,14 +1,14 @@
# 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 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"
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
index 6b40305..236b91a 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
@@ -1,6 +1,6 @@
-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..d48ac7e 100644
--- a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
-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_item/__init__.py b/erpnext/buying/doctype/supplier_quotation_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/__init__.py
+++ b/erpnext/buying/doctype/supplier_quotation_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..672de1a 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py
+++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class SupplierQuotationItem(Document):
pass
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
index e956afd..3bcc0de 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
@@ -1,15 +1,19 @@
-# -*- 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 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 8e5cce5..5d69326 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
index a5f05ea..49e3351 100644
--- a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestSupplierScorecard(unittest.TestCase):
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json
index 2623585..3668b25 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json
@@ -1,184 +1,70 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "field:criteria_name",
- "beta": 0,
- "creation": "2017-05-29 01:32:43.064891",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:criteria_name",
+ "creation": "2017-05-29 01:32:43.064891",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "criteria_name",
+ "max_score",
+ "formula",
+ "weight"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "criteria_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Criteria 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,
+ "fieldname": "criteria_name",
+ "fieldtype": "Data",
+ "label": "Criteria Name",
+ "reqd": 1,
"unique": 1
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "100",
- "fieldname": "max_score",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Max Score",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "default": "100",
+ "fieldname": "max_score",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Max Score",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "formula",
- "fieldtype": "Small Text",
- "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": "Criteria Formula",
- "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": "formula",
+ "fieldtype": "Small Text",
+ "ignore_xss_filter": 1,
+ "in_list_view": 1,
+ "label": "Criteria Formula",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "weight",
- "fieldtype": "Percent",
- "hidden": 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": "Criteria Weight",
- "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": "weight",
+ "fieldtype": "Percent",
+ "label": "Criteria Weight"
}
- ],
- "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-01-22 10:47:00.000822",
- "modified_by": "Administrator",
- "module": "Buying",
- "name": "Supplier Scorecard Criteria",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "links": [],
+ "modified": "2021-11-11 18:34:58.477648",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Criteria",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
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..7cd18c3 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
@@ -1,13 +1,14 @@
-# -*- 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 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 3babfc8..dacc982 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestSupplierScorecardCriteria(unittest.TestCase):
def test_variables_exist(self):
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 cc345e9..c247241 100644
--- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
@@ -1,14 +1,17 @@
-# -*- 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 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):
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..005cd79 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..3a6de59 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
@@ -1,9 +1,9 @@
-# -*- 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 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..8d66e64 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
@@ -1,9 +1,9 @@
-# -*- 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 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..f13eb5b 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
@@ -1,9 +1,9 @@
-# -*- 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 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 678855a..11ebe6d 100644
--- a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class SupplierScorecardStanding(Document):
pass
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..bd1b0ad 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 89a6459..217aadba6 100644
--- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
@@ -1,14 +1,15 @@
-# -*- 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
+
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 +19,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)
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 14b8710..4d75981 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
@@ -1,12 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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):
diff --git a/erpnext/buying/form_tour/buying_settings/buying_settings.json b/erpnext/buying/form_tour/buying_settings/buying_settings.json
new file mode 100644
index 0000000..fa8c80d
--- /dev/null
+++ b/erpnext/buying/form_tour/buying_settings/buying_settings.json
@@ -0,0 +1,77 @@
+{
+ "creation": "2021-07-28 11:51:42.319984",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:06:56.414584",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Buying Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Buying Settings",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "When a Supplier is saved, system generates a unique identity or name for that Supplier which can be used to refer the Supplier in various Buying transactions.",
+ "field": "",
+ "fieldname": "supp_master_name",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Supplier Naming By",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Supplier Naming By"
+ },
+ {
+ "description": "Configure what should be the default value of Supplier Group when creating a new Supplier.",
+ "field": "",
+ "fieldname": "supplier_group",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Default Supplier Group",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Default Supplier Group"
+ },
+ {
+ "description": "Item prices will be fetched from this Price List.",
+ "field": "",
+ "fieldname": "buying_price_list",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Default Buying Price List",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Default Buying Price List"
+ },
+ {
+ "description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice or a Purchase Receipt directly without creating a Purchase Order first.",
+ "field": "",
+ "fieldname": "po_required",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Is Purchase Order Required for Purchase Invoice & Receipt Creation?",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Purchase Order Required"
+ },
+ {
+ "description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first.",
+ "field": "",
+ "fieldname": "pr_required",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Is Purchase Receipt Required for Purchase Invoice Creation?",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Purchase Receipt Required"
+ }
+ ],
+ "title": "Buying Settings"
+}
\ No newline at end of file
diff --git a/erpnext/buying/form_tour/purchase_order/purchase_order.json b/erpnext/buying/form_tour/purchase_order/purchase_order.json
new file mode 100644
index 0000000..3cc88fb
--- /dev/null
+++ b/erpnext/buying/form_tour/purchase_order/purchase_order.json
@@ -0,0 +1,82 @@
+{
+ "creation": "2021-07-29 14:11:58.271113",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:11:31.436135",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order",
+ "owner": "Administrator",
+ "reference_doctype": "Purchase Order",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "Select a Supplier",
+ "field": "",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Supplier",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Supplier"
+ },
+ {
+ "description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
+ "field": "",
+ "fieldname": "schedule_date",
+ "fieldtype": "Date",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Required By",
+ "parent_field": "",
+ "position": "Left",
+ "title": "Required By"
+ },
+ {
+ "description": "Items to be purchased can be added here.",
+ "field": "",
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Items",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Items Table"
+ },
+ {
+ "child_doctype": "Purchase Order Item",
+ "description": "Enter the Item Code.",
+ "field": "",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "has_next_condition": 1,
+ "is_table_field": 1,
+ "label": "Item Code",
+ "next_step_condition": "eval: doc.item_code",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Item Code"
+ },
+ {
+ "child_doctype": "Purchase Order Item",
+ "description": "Enter the required quantity for the material.",
+ "field": "",
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "has_next_condition": 0,
+ "is_table_field": 1,
+ "label": "Quantity",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Bottom",
+ "title": "Quantity"
+ }
+ ],
+ "title": "Purchase Order"
+}
\ No newline at end of file
diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json
index 887f85b..84e97a2 100644
--- a/erpnext/buying/module_onboarding/buying/buying.json
+++ b/erpnext/buying/module_onboarding/buying/buying.json
@@ -19,7 +19,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
"idx": 0,
"is_complete": 0,
- "modified": "2020-07-08 14:05:28.273641",
+ "modified": "2021-08-24 18:13:42.463776",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",
@@ -29,22 +29,10 @@
"step": "Introduction to Buying"
},
{
- "step": "Create a Supplier"
- },
- {
- "step": "Setup your Warehouse"
- },
- {
- "step": "Create a Product"
- },
- {
"step": "Create a Material Request"
},
{
"step": "Create your first Purchase Order"
- },
- {
- "step": "Buying Settings"
}
],
"subtitle": "Products, Purchases, Analysis, and more.",
diff --git a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
index 9dc493d..28e86ab 100644
--- a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
+++ b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
@@ -1,19 +1,21 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Material Request",
"creation": "2020-05-15 14:39:09.818764",
+ "description": "# Track Material Request\n\n\nAlso known as Purchase Request or an Indent, is a document identifying a requirement of a set of items (products or services) for various purposes like procurement, transfer, issue, or manufacturing. Once the Material Request is validated, a purchase manager can take the next actions for purchasing items like requesting RFQ from a supplier or directly placing an order with an identified Supplier.\n\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-15 14:39:09.818764",
+ "modified": "2021-08-24 18:08:08.347501",
"modified_by": "Administrator",
"name": "Create a Material Request",
"owner": "Administrator",
"reference_document": "Material Request",
+ "show_form_tour": 1,
"show_full_form": 1,
- "title": "Create a Material Request",
+ "title": "Track Material Request",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
index 9dbed23..18a3931 100644
--- a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
+++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
@@ -1,19 +1,21 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Purchase Order",
"creation": "2020-05-12 18:17:49.976035",
+ "description": "# Create first Purchase Order\n\nPurchase Order is at the heart of your buying transactions. In ERPNext, Purchase Order can can be created against a Purchase Material Request (indent) and Supplier Quotation as well. Purchase Orders is also linked to Purchase Receipt and Purchase Invoices, allowing you to keep a birds-eye view on your purchase deals.\n\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-12 18:31:56.856112",
+ "modified": "2021-08-24 18:08:08.936484",
"modified_by": "Administrator",
"name": "Create your first Purchase Order",
"owner": "Administrator",
"reference_document": "Purchase Order",
+ "show_form_tour": 0,
"show_full_form": 0,
- "title": "Create your first Purchase Order",
+ "title": "Create first Purchase Order",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
index fd98fdd..01ac8b8 100644
--- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
+++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
@@ -1,19 +1,22 @@
{
- "action": "Watch Video",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s walk-through few Buying Settings",
"creation": "2020-05-06 15:37:09.477765",
+ "description": "# Buying Settings\n\n\nBuying module\u2019s features are highly configurable as per your business needs. Buying Settings is the place where you can set your preferences for:\n\n- Supplier naming and default values\n- Billing and shipping preference in buying transactions\n\n\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
+ "is_single": 1,
"is_skipped": 0,
- "modified": "2020-05-12 18:25:08.509900",
+ "modified": "2021-08-24 18:08:08.345735",
"modified_by": "Administrator",
"name": "Introduction to Buying",
"owner": "Administrator",
- "show_full_form": 0,
- "title": "Introduction to Buying",
+ "reference_document": "Buying Settings",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Buying Settings",
"validate_action": 1,
"video_url": "https://youtu.be/efFajTTQBa8"
}
\ No newline at end of file
diff --git a/erpnext/buying/page/__init__.py b/erpnext/buying/page/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/buying/page/__init__.py
+++ b/erpnext/buying/page/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/buying/print_format_field_template/__init__.py
similarity index 100%
rename from erpnext/buying/doctype/supplier_item_group/__init__.py
rename to erpnext/buying/print_format_field_template/__init__.py
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/buying/print_format_field_template/purchase_order_taxes/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__init__.py
copy to erpnext/buying/print_format_field_template/purchase_order_taxes/__init__.py
diff --git a/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json b/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json
new file mode 100644
index 0000000..73b7730
--- /dev/null
+++ b/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:07:19.253457",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Purchase Order",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:07:19.253457",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__init__.py
copy to erpnext/buying/print_format_field_template/supplier_quotation_taxes/__init__.py
diff --git a/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json
new file mode 100644
index 0000000..2be17a1
--- /dev/null
+++ b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:09:08.103919",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Supplier Quotation",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:09:08.103919",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Quotation Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
index 99bcbe6..295a19d 100644
--- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
@@ -1,11 +1,12 @@
# 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 flt
+
def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
index c36083f..84de8c6 100644
--- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -1,16 +1,19 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 = {
@@ -41,7 +44,6 @@
pr = make_purchase_receipt(po.name)
pr.get("items")[0].cost_center = "Main - _TPC"
pr.submit()
- frappe.db.commit()
date_obj = datetime.date(datetime.now())
po.load_from_db()
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.py b/erpnext/buying/report/purchase_analytics/purchase_analytics.py
index 0f94947..6a84d91 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.py
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.py
@@ -1,8 +1,9 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 bda1727..9dd9121 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
@@ -1,11 +1,13 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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,15 +40,16 @@
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']:
+ 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"):
+ if filters.get('status'):
conditions += " and po.status in %(status)s"
+ if filters.get('project'):
+ conditions += " and poi.project = %(project)s"
+
return conditions
def get_data(conditions, filters):
@@ -54,6 +57,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 +176,12 @@
"fieldtype": "Link",
"options": "Supplier",
"width": 130
+ },{
+ "label": _("Project"),
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "options": "Project",
+ "width": 130
}]
if not filters.get("group_by_po"):
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 095a443..21643a8 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
@@ -1,10 +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
+
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/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..f98e5f1 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
@@ -1,11 +1,13 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 9a45972..8e5c2f9 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
@@ -1,10 +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/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..67e275f 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
@@ -1,10 +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):
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 cb304a1..144523a 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
@@ -2,13 +2,18 @@
# Embedded file name: /Users/anuragmishra/frappe-develop/apps/erpnext/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
# 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):
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 96cacb6..6b605ad 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
@@ -1,10 +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):
if filters.from_date >= filters.to_date:
frappe.msgprint(_("To Date must be greater than From Date"))
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..3c203ac 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
@@ -2,12 +2,19 @@
# Embedded file name: /Users/anuragmishra/frappe-develop/apps/erpnext/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
# 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.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
index a5a3105..65f9ce3 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
@@ -1,13 +1,16 @@
# 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 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 [], []
diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py
index 1792863..66c60d5 100644
--- a/erpnext/buying/utils.py
+++ b/erpnext/buying/utils.py
@@ -1,14 +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
-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"""
diff --git a/erpnext/buying/workspace/buying/buying.json b/erpnext/buying/workspace/buying/buying.json
index 6c91e81..380ef36 100644
--- a/erpnext/buying/workspace/buying/buying.json
+++ b/erpnext/buying/workspace/buying/buying.json
@@ -1,27 +1,18 @@
{
- "cards_label": "",
- "category": "",
"charts": [
{
"chart_name": "Purchase Order Trends",
"label": "Purchase Order Trends"
}
],
- "charts_label": "",
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Buying\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Purchase Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Buying\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items & Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier Scorecard\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Regional\", \"col\": 4}}]",
"creation": "2020-01-28 11:50:26.195467",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "buying",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Buying",
"links": [
{
@@ -518,15 +509,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:56.218427",
+ "modified": "2021-08-05 12:15:56.218428",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",
- "onboarding": "Buying",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
@@ -572,6 +560,5 @@
"type": "Dashboard"
}
],
- "shortcuts_label": "",
"title": "Buying"
}
\ No newline at end of file
diff --git a/erpnext/commands/__init__.py b/erpnext/commands/__init__.py
index 2276c73..5931119 100644
--- a/erpnext/commands/__init__.py
+++ b/erpnext/commands/__init__.py
@@ -1,10 +1,10 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
-from __future__ import unicode_literals, absolute_import, print_function
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)
diff --git a/erpnext/communication/doctype/communication_medium/communication_medium.py b/erpnext/communication/doctype/communication_medium/communication_medium.py
index f233da0..6dfdb73 100644
--- a/erpnext/communication/doctype/communication_medium/communication_medium.py
+++ b/erpnext/communication/doctype/communication_medium/communication_medium.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..b65eba7 100644
--- a/erpnext/communication/doctype/communication_medium_timeslot/communication_medium_timeslot.py
+++ b/erpnext/communication/doctype/communication_medium_timeslot/communication_medium_timeslot.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 CommunicationMediumTimeslot(Document):
pass
diff --git a/erpnext/config/education.py b/erpnext/config/education.py
index 1c8ab10..d718a94 100644
--- a/erpnext/config/education.py
+++ b/erpnext/config/education.py
@@ -1,6 +1,6 @@
-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..f4675e7 100644
--- a/erpnext/config/projects.py
+++ b/erpnext/config/projects.py
@@ -1,6 +1,6 @@
-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 ce6a2a4..3851cf4 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1,28 +1,60 @@
# 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, 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 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,
+)
+
+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,
+ get_party_gle_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.accounts.party import get_party_account_currency, validate_party_frozen_disabled, get_party_gle_currency
-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 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
+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
@@ -115,11 +147,6 @@
self.validate_currency()
self.validate_party_account_currency()
- if self.doctype == 'Purchase Invoice':
- self.calculate_paid_amount()
- # apply tax withholding only if checked and applicable
- self.set_tax_withholding()
-
if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
@@ -134,6 +161,11 @@
self.set_inter_company_account()
+ if self.doctype == 'Purchase Invoice':
+ self.calculate_paid_amount()
+ # apply tax withholding only if checked and applicable
+ self.set_tax_withholding()
+
validate_regional(self)
if self.doctype != 'Material Request':
@@ -220,7 +252,12 @@
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
calculate_taxes_and_totals(self)
- if self.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
+ if self.doctype in (
+ 'Sales Order',
+ 'Delivery Note',
+ 'Sales Invoice',
+ 'POS Invoice',
+ ):
self.calculate_commission()
self.calculate_contribution()
@@ -494,7 +531,8 @@
'is_opening': self.get("is_opening") or "No",
'party_type': None,
'party': None,
- 'project': self.get("project")
+ 'project': self.get("project"),
+ 'post_net_value': args.get('post_net_value')
})
accounting_dimensions = get_accounting_dimensions()
@@ -653,13 +691,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
@@ -678,7 +720,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:
@@ -697,7 +739,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)
)
@@ -771,7 +813,6 @@
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
- self.update_allocated_advance_taxes_on_cancel()
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
@@ -779,6 +820,38 @@
if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
unlink_ref_doc_from_payment_entries(self)
+ if self.doctype == "Sales Order":
+ self.unlink_ref_doc_from_po()
+
+ def unlink_ref_doc_from_po(self):
+ so_items = []
+ for item in self.items:
+ so_items.append(item.name)
+
+ linked_po = list(set(frappe.get_all(
+ 'Purchase Order Item',
+ filters = {
+ 'sales_order': self.name,
+ 'sales_order_item': ['in', so_items],
+ 'docstatus': ['<', 2]
+ },
+ pluck='parent'
+ )))
+
+ if linked_po:
+ frappe.db.set_value(
+ 'Purchase Order Item', {
+ 'sales_order': self.name,
+ 'sales_order_item': ['in', so_items],
+ 'docstatus': ['<', 2]
+ },{
+ 'sales_order': None,
+ 'sales_order_item': None
+ }
+ )
+
+ frappe.msgprint(_("Purchase Orders {0} are un-linked").format("\n".join(linked_po)))
+
def get_tax_map(self):
tax_map = {}
for tax in self.get('taxes'):
@@ -787,29 +860,6 @@
return tax_map
- def update_allocated_advance_taxes_on_cancel(self):
- if self.get('advances'):
- tax_accounts = [d.account_head for d in self.get('taxes')]
- allocated_tax_map = frappe._dict(frappe.get_all('GL Entry', fields=['account', 'sum(credit - debit)'],
- filters={'voucher_no': self.name, 'account': ('in', tax_accounts)},
- group_by='account', as_list=1))
-
- tax_map = self.get_tax_map()
-
- for pe in self.get('advances'):
- if pe.reference_type == 'Payment Entry':
- pe = frappe.get_doc('Payment Entry', pe.reference_name)
- for tax in pe.get('taxes'):
- allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head)
- if allocated_amount > tax.tax_amount:
- allocated_amount = tax.tax_amount
-
- if allocated_amount:
- frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount',
- tax.allocated_amount - allocated_amount)
- tax_map[tax.account_head] -= allocated_amount
- allocated_tax_map[tax.account_head] -= allocated_amount
-
def get_amount_and_base_amount(self, item, enable_discount_accounting):
amount = item.net_amount
base_amount = item.base_net_amount
@@ -893,97 +943,94 @@
}, item=self)
)
- def allocate_advance_taxes(self, gl_entries):
- tax_map = self.get_tax_map()
- for pe in self.get("advances"):
- if pe.reference_type == "Payment Entry" and \
- frappe.db.get_value('Payment Entry', pe.reference_name, 'advance_tax_account'):
- pe = frappe.get_doc("Payment Entry", pe.reference_name)
- for tax in pe.get("taxes"):
- account_currency = get_account_currency(tax.account_head)
-
- if self.doctype == "Purchase Invoice":
- dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
- rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
- else:
- dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
- rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
-
- party = self.supplier if self.doctype == "Purchase Invoice" else self.customer
- unallocated_amount = tax.tax_amount - tax.allocated_amount
- if tax_map.get(tax.account_head):
- amount = tax_map.get(tax.account_head)
- if amount < unallocated_amount:
- unallocated_amount = amount
-
- gl_entries.append(
- self.get_gl_dict({
- "account": tax.account_head,
- "against": party,
- dr_or_cr: unallocated_amount,
- dr_or_cr + "_in_account_currency": unallocated_amount
- if account_currency==self.company_currency
- else unallocated_amount,
- "cost_center": tax.cost_center
- }, account_currency, item=tax))
-
- gl_entries.append(
- self.get_gl_dict({
- "account": pe.advance_tax_account,
- "against": party,
- rev_dr_cr: unallocated_amount,
- rev_dr_cr + "_in_account_currency": unallocated_amount
- if account_currency==self.company_currency
- else unallocated_amount,
- "cost_center": tax.cost_center
- }, account_currency, item=tax))
-
- frappe.db.set_value("Advance Taxes and Charges", tax.name, "allocated_amount",
- tax.allocated_amount + unallocated_amount)
-
- tax_map[tax.account_head] -= unallocated_amount
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_allowance_for
+
item_allowance = {}
global_qty_allowance, global_amount_allowance = None, None
+ role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
+ 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 = self.get_billed_amount_for_item(item, item_ref_dn, based_on)
- 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), indicator="orange", alert=True)
+
+ def get_billed_amount_for_item(self, item, item_ref_dn, based_on):
+ '''
+ Returns Sum of Amount of
+ Sales/Purchase Invoice Items
+ that are linked to `item_ref_dn` (`dn_detail` / `pr_detail`)
+ that are submitted OR not submitted but are under current invoice
+ '''
+
+ from frappe.query_builder import Criterion
+ from frappe.query_builder.functions import Sum
+
+ item_doctype = frappe.qb.DocType(item.doctype)
+ based_on_field = frappe.qb.Field(based_on)
+ join_field = frappe.qb.Field(item_ref_dn)
+
+ result = (
+ frappe.qb.from_(item_doctype)
+ .select(Sum(based_on_field))
+ .where(
+ join_field == item.get(item_ref_dn)
+ ).where(
+ Criterion.any([ # select all items from other invoices OR current invoices
+ Criterion.all([ # for selecting items from other invoices
+ item_doctype.docstatus == 1,
+ item_doctype.parent != self.name
+ ]),
+ Criterion.all([ # for selecting items from current invoice, that are linked to same reference
+ item_doctype.docstatus == 0,
+ item_doctype.parent == self.name,
+ item_doctype.name != item.name
+ ])
+ ])
+ )
+ ).run()
+
+ return result[0][0] if result else 0
def throw_overbill_exception(self, item, max_allowed_amt):
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
@@ -1000,7 +1047,7 @@
stock_items = [r[0] for r in frappe.db.sql("""
select name from `tabItem`
where name in (%s) and is_stock_item=1
- """ % (", ".join((["%s"] * len(item_codes))),), item_codes)]
+ """ % (", ".join(["%s"] * len(item_codes)),), item_codes)]
return stock_items
@@ -1223,7 +1270,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):
@@ -1321,8 +1368,8 @@
total = 0
base_total = 0
for d in self.get("payment_schedule"):
- total += flt(d.payment_amount)
- base_total += flt(d.base_payment_amount)
+ total += flt(d.payment_amount, d.precision("payment_amount"))
+ base_total += flt(d.base_payment_amount, d.precision("base_payment_amount"))
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
grand_total = self.get("rounded_total") or self.grand_total
@@ -1338,8 +1385,9 @@
else:
grand_total -= self.get("total_advance")
base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
- if total != flt(grand_total, self.precision("grand_total")) or \
- base_total != flt(base_grand_total, self.precision("base_grand_total")):
+
+ if flt(total, self.precision("grand_total")) - flt(grand_total, self.precision("grand_total")) > 0.1 or \
+ flt(base_total, self.precision("base_grand_total")) - flt(base_grand_total, self.precision("base_grand_total")) > 0.1:
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
def is_rounded_total_disabled(self):
@@ -1380,6 +1428,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)
@@ -1543,7 +1652,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"
@@ -1578,27 +1687,72 @@
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."""
+ today = getdate()
- 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{doctype}` invoice SET invoice.status = 'Overdue'
+ WHERE invoice.docstatus = 1
+ AND invoice.status REGEXP '^Unpaid|^Partly Paid'
+ AND invoice.outstanding_amount > 0
+ AND (
+ {or_condition}
+ (
+ (
+ CASE
+ WHEN invoice.party_account_currency = invoice.currency
+ THEN (
+ CASE
+ WHEN invoice.disable_rounded_total
+ THEN invoice.grand_total
+ ELSE invoice.rounded_total
+ END
+ )
+ ELSE (
+ CASE
+ WHEN invoice.disable_rounded_total
+ THEN invoice.base_grand_total
+ ELSE invoice.base_rounded_total
+ END
+ )
+ END
+ ) - invoice.outstanding_amount
+ ) < (
+ SELECT SUM(
+ CASE
+ WHEN invoice.party_account_currency = invoice.currency
+ THEN ps.payment_amount
+ ELSE ps.base_payment_amount
+ END
+ )
+ FROM `tabPayment Schedule` ps
+ WHERE ps.parent = invoice.name
+ AND ps.due_date < %(today)s
+ )
+ )
+ """.format(
+ doctype=doctype,
+ or_condition=(
+ "invoice.is_pos AND invoice.due_date < %(today)s OR"
+ if doctype == "Sales Invoice"
+ else ""
+ )
+ ), {"today": today}
+ )
@frappe.whitelist()
def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
@@ -1618,7 +1772,7 @@
@frappe.whitelist()
def get_payment_term_details(term, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
term_details = frappe._dict()
- if isinstance(term, text_type):
+ if isinstance(term, str):
term = frappe.get_doc("Payment Term", term)
else:
term_details.payment_term = term.payment_term
@@ -1767,7 +1921,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":
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 974ade3..a3d2502 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -1,25 +1,24 @@
# 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 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 import ValidationError, _, msgprint
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 QtyMismatchError(ValidationError):
+ pass
class BuyingController(StockController, Subcontracting):
@@ -364,19 +363,15 @@
def validate_accepted_rejected_qty(self):
for d in self.get("items"):
self.validate_negative_quantity(d, ["received_qty","qty", "rejected_qty"])
- if not flt(d.received_qty) and flt(d.qty):
- d.received_qty = flt(d.qty) - flt(d.rejected_qty)
- elif not flt(d.qty) and flt(d.rejected_qty):
- d.qty = flt(d.received_qty) - flt(d.rejected_qty)
+ if not flt(d.received_qty) and (flt(d.qty) or flt(d.rejected_qty)):
+ d.received_qty = flt(d.qty) + flt(d.rejected_qty)
- elif not flt(d.rejected_qty):
- d.rejected_qty = flt(d.received_qty) - flt(d.qty)
-
- val = flt(d.qty) + flt(d.rejected_qty)
# Check Received Qty = Accepted Qty + Rejected Qty
+ val = flt(d.qty) + flt(d.rejected_qty)
if (flt(val, d.precision("received_qty")) != flt(d.received_qty, d.precision("received_qty"))):
- frappe.throw(_("Accepted + Rejected Qty must be equal to Received quantity for Item {0}").format(d.item_code))
+ message = _("Row #{0}: Received Qty must be equal to Accepted + Rejected Qty for Item {1}").format(d.idx, d.item_code)
+ frappe.throw(msg=message, title=_("Mismatch"), exc=QtyMismatchError)
def validate_negative_quantity(self, item_row, field_list):
if self.is_return:
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 8c361a2..2bad6f8 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -1,13 +1,14 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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
@@ -28,7 +29,7 @@
return make_variant_based_on_manufacturer(item_template, manufacturer,
manufacturer_part_no)
else:
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
if not args:
@@ -52,7 +53,7 @@
return variant
def validate_item_variant_attributes(item, args=None):
- if isinstance(item, string_types):
+ if isinstance(item, str):
item = frappe.get_doc('Item', item)
if not args:
@@ -154,7 +155,7 @@
@frappe.whitelist()
def create_variant(item, args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
template = frappe.get_doc("Item", item)
@@ -177,7 +178,7 @@
@frappe.whitelist()
def enqueue_multiple_variant_creation(item, args):
# There can be innumerable attribute combinations, enqueue
- if isinstance(args, string_types):
+ if isinstance(args, str):
variants = json.loads(args)
total_variants = 1
for key in variants:
@@ -194,7 +195,7 @@
def create_multiple_variants(item, args):
count = 0
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
args_set = generate_keyed_value_combinations(args)
diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py
index e08c400..cf9de52 100644
--- a/erpnext/controllers/print_settings.py
+++ b/erpnext/controllers/print_settings.py
@@ -1,9 +1,7 @@
# 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 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 4b4c8be..dc04dab 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -1,15 +1,18 @@
# 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 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()
@@ -128,7 +131,8 @@
return frappe.db.sql("""select {field} from `tabSupplier`
where docstatus < 2
and ({key} like %(txt)s
- or supplier_name like %(txt)s) and disabled=0
+ or supplier_name like %(txt)s) and disabled=0
+ and (on_hold = 0 or (on_hold = 1 and CURDATE() > release_date))
{mcond}
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@@ -206,12 +210,15 @@
meta = frappe.get_meta("Item", cached=True)
searchfields = meta.get_search_fields()
- if "description" in searchfields:
- searchfields.remove("description")
+ # these are handled separately
+ ignored_search_fields = ("item_name", "description")
+ for ignored_field in ignored_search_fields:
+ if ignored_field in searchfields:
+ searchfields.remove(ignored_field)
columns = ''
extra_searchfields = [field for field in searchfields
- if not field in ["name", "item_group", "description"]]
+ if not field in ["name", "item_group", "description", "item_name"]]
if extra_searchfields:
columns = ", " + ", ".join(extra_searchfields)
@@ -220,27 +227,36 @@
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:
# scan description only if items are less than 50000
description_cond = 'or tabItem.description LIKE %(txt)s'
- return frappe.db.sql("""select tabItem.name,
- if(length(tabItem.item_name) > 40,
- concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
- tabItem.item_group,
+ return frappe.db.sql("""select
+ tabItem.name, tabItem.item_name, tabItem.item_group,
if(length(tabItem.description) > 40, \
concat(substr(tabItem.description, 1, 40), "..."), description) as description
{columns}
@@ -304,7 +320,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,10 +533,16 @@
@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 = []
+ or_filters = []
+ fields = ['name']
+
+ searchfields = frappe.get_meta(doctype).get_search_fields()
meta = frappe.get_meta(doctype)
if meta.is_tree:
@@ -532,8 +554,9 @@
if meta.has_field('company'):
query_filters.append(['company', '=', filters.get('company')])
- if txt:
- query_filters.append([searchfield, 'LIKE', "%%%s%%" % txt])
+ for field in searchfields:
+ or_filters.append([field, 'LIKE', "%%%s%%" % txt])
+ fields.append(field)
if dimension_filters:
if dimension_filters['allow_or_restrict'] == 'Allow':
@@ -548,10 +571,9 @@
query_filters.append(['name', query_selector, dimensions])
- output = frappe.get_all(doctype, filters=query_filters)
- result = [d.name for d in output]
+ output = frappe.get_list(doctype, fields=fields, filters=query_filters, or_filters=or_filters, as_list=1)
- return [(d,) for d in set(result)]
+ return [tuple(d) for d in set(output)]
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
@@ -681,34 +703,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 5ee1f2f..df3c5f1 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.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, 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 +66,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 +238,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")
@@ -394,19 +398,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,
@@ -417,7 +408,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 fc2cc97..cc773b7 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -1,19 +1,19 @@
# 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 cint, flt, cstr, get_link_to_form, nowtime
-from frappe import _, bold, 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):
@@ -120,13 +120,27 @@
self.in_words = money_in_words(amount, self.currency)
def calculate_commission(self):
- if self.meta.get_field("commission_rate"):
- self.round_floats_in(self, ["base_net_total", "commission_rate"])
- if self.commission_rate > 100.0:
- throw(_("Commission rate cannot be greater than 100"))
+ if not self.meta.get_field("commission_rate"):
+ return
- self.total_commission = flt(self.base_net_total * self.commission_rate / 100.0,
- self.precision("total_commission"))
+ self.round_floats_in(
+ self, ("amount_eligible_for_commission", "commission_rate")
+ )
+
+ if not (0 <= self.commission_rate <= 100.0):
+ throw("{} {}".format(
+ _(self.meta.get_label("commission_rate")),
+ _("must be between 0 and 100"),
+ ))
+
+ self.amount_eligible_for_commission = sum(
+ item.base_net_amount for item in self.items if item.grant_commission
+ )
+
+ self.total_commission = flt(
+ self.amount_eligible_for_commission * self.commission_rate / 100.0,
+ self.precision("total_commission")
+ )
def calculate_contribution(self):
if not self.meta.get_field("sales_team"):
@@ -138,7 +152,7 @@
self.round_floats_in(sales_person)
sales_person.allocated_amount = flt(
- self.base_net_total * sales_person.allocated_percentage / 100.0,
+ self.amount_eligible_for_commission * sales_person.allocated_percentage / 100.0,
self.precision("allocated_amount", sales_person))
if sales_person.commission_rate:
@@ -362,7 +376,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 [])
@@ -371,18 +385,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():
@@ -557,6 +572,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 7b24e50..76a7cda 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -1,11 +1,12 @@
# 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 flt, comma_or, nowdate, getdate, now
from frappe import _
from frappe.model.document import Document
+from frappe.utils import comma_or, flt, getdate, now, nowdate
+
class OverAllowanceError(frappe.ValidationError): pass
@@ -214,11 +215,14 @@
overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
item[args['target_ref_field']]) * 100
- if overflow_percent - allowance > 0.01 and role not in frappe.get_roles():
+ if overflow_percent - allowance > 0.01:
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+allowance)/100)
item['reduce_by'] = item[args['target_field']] - item['max_allowed']
- self.limits_crossed_error(args, item, qty_or_amount)
+ if role not in frappe.get_roles():
+ self.limits_crossed_error(args, item, qty_or_amount)
+ else:
+ self.warn_about_bypassing_with_role(item, qty_or_amount, role)
def limits_crossed_error(self, args, item, qty_or_amount):
'''Raise exception for limits crossed'''
@@ -236,6 +240,19 @@
frappe.bold(item.get('item_code'))
) + '<br><br>' + action_msg, OverAllowanceError, title = _('Limit Crossed'))
+ def warn_about_bypassing_with_role(self, item, qty_or_amount, role):
+ action = _("Over Receipt/Delivery") if qty_or_amount == "qty" else _("Overbilling")
+
+ msg = (_("{} of {} {} ignored for item {} because you have {} role.")
+ .format(
+ action,
+ _(item["target_ref_field"].title()),
+ frappe.bold(item["reduce_by"]),
+ frappe.bold(item.get('item_code')),
+ role)
+ )
+ frappe.msgprint(msg, indicator="orange", alert=True)
+
def update_qty(self, update_modified=True):
"""Updates qty or amount at row level
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 17707ec..7073e32 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -5,16 +5,19 @@
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
-from erpnext.stock.stock_ledger import get_valuation_rate
+from erpnext.stock.stock_ledger import get_items_to_be_repost, get_valuation_rate
class QualityInspectionRequiredError(frappe.ValidationError): pass
@@ -76,8 +79,15 @@
def clean_serial_nos(self):
for row in self.get("items"):
if hasattr(row, "serial_no") and row.serial_no:
- # replace commas by linefeed and remove all spaces in string
- row.serial_no = row.serial_no.replace(",", "\n").replace(" ", "")
+ # replace commas by linefeed
+ row.serial_no = row.serial_no.replace(",", "\n")
+
+ # strip preceeding and succeeding spaces for each SN
+ # (SN could have valid spaces in between e.g. SN - 123 - 2021)
+ serial_no_list = row.serial_no.split("\n")
+ serial_no_list = [sn.strip() for sn in serial_no_list]
+
+ row.serial_no = "\n".join(serial_no_list)
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
default_cost_center=None):
@@ -124,7 +134,7 @@
"against": expense_account,
"cost_center": item_row.cost_center,
"project": item_row.project or self.get('project'),
- "remarks": self.get("remarks") or "Accounting Entry for Stock",
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": flt(sle.stock_value_difference, precision),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
@@ -133,7 +143,7 @@
"account": expense_account,
"against": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
- "remarks": self.get("remarks") or "Accounting Entry for Stock",
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(sle.stock_value_difference, precision),
"project": item_row.get("project") or self.get("project"),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
@@ -534,7 +544,12 @@
"company": self.company
})
if future_sle_exists(args):
- create_repost_item_valuation_entry(args)
+ item_based_reposting = cint(frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting"))
+ if item_based_reposting:
+ create_item_wise_repost_entries(voucher_type=self.doctype, voucher_no=self.name)
+ else:
+ create_repost_item_valuation_entry(args)
+
@frappe.whitelist()
def make_quality_inspections(doctype, docname, items):
@@ -588,7 +603,7 @@
data = frappe.db.sql("""
select item_code, warehouse, count(name) as total_row
- from `tabStock Ledger Entry`
+ from `tabStock Ledger Entry` force index (item_warehouse)
where
({})
and timestamp(posting_date, posting_time)
@@ -666,5 +681,38 @@
repost_entry.company = args.company
repost_entry.allow_zero_rate = args.allow_zero_rate
repost_entry.flags.ignore_links = True
+ repost_entry.flags.ignore_permissions = True
repost_entry.save()
repost_entry.submit()
+
+
+def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=False):
+ """Using a voucher create repost item valuation records for all item-warehouse pairs."""
+
+ stock_ledger_entries = get_items_to_be_repost(voucher_type, voucher_no)
+
+ distinct_item_warehouses = set()
+ repost_entries = []
+
+ for sle in stock_ledger_entries:
+ item_wh = (sle.item_code, sle.warehouse)
+ if item_wh in distinct_item_warehouses:
+ continue
+ distinct_item_warehouses.add(item_wh)
+
+ repost_entry = frappe.new_doc("Repost Item Valuation")
+ repost_entry.based_on = "Item and Warehouse"
+ repost_entry.voucher_type = voucher_type
+ repost_entry.voucher_no = voucher_no
+
+ repost_entry.item_code = sle.item_code
+ repost_entry.warehouse = sle.warehouse
+ repost_entry.posting_date = sle.posting_date
+ repost_entry.posting_time = sle.posting_time
+ repost_entry.allow_zero_rate = allow_zero_rate
+ repost_entry.flags.ignore_links = True
+ repost_entry.flags.ignore_permissions = True
+ repost_entry.submit()
+ repost_entries.append(repost_entry)
+
+ return repost_entries
diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py
index 969829f..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:
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 7c6d355..746c6fd 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -1,16 +1,23 @@
# 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 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):
@@ -43,6 +50,7 @@
self.initialize_taxes()
self.determine_exclusive_rate()
self.calculate_net_total()
+ self.calculate_shipping_charges()
self.calculate_taxes()
self.manipulate_grand_total_for_inclusive_tax()
self.calculate_totals()
@@ -169,7 +177,7 @@
self.doc.round_floats_in(tax)
def determine_exclusive_rate(self):
- if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
+ if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
return
for item in self.doc.get("items"):
@@ -251,8 +259,15 @@
self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
+ def calculate_shipping_charges(self):
+ if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
+ shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
+ shipping_rule.apply(self.doc)
+
def calculate_taxes(self):
- self.doc.rounding_adjustment = 0
+ if not self.doc.get('is_consolidated'):
+ self.doc.rounding_adjustment = 0
+
# maintain actual tax rate based on idx
actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
@@ -304,7 +319,9 @@
# adjust Discount Amount loss in last tax iteration
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
- and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
+ and self.doc.discount_amount \
+ and self.doc.apply_discount_on == "Grand Total" \
+ and not self.doc.get('is_consolidated'):
self.doc.rounding_adjustment = flt(self.doc.grand_total
- flt(self.doc.discount_amount) - tax.total,
self.doc.precision("rounding_adjustment"))
@@ -397,11 +414,16 @@
self.doc.rounding_adjustment = diff
def calculate_totals(self):
- self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
- if self.doc.get("taxes") else flt(self.doc.net_total)
+ if self.doc.get("taxes"):
+ self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
+ else:
+ self.doc.grand_total = flt(self.doc.net_total)
- self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
+ if self.doc.get("taxes"):
+ self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
- flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
+ else:
+ self.doc.total_taxes_and_charges = 0.0
self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
@@ -438,19 +460,20 @@
self.doc.total_net_weight += d.total_weight
def set_rounded_total(self):
- if self.doc.meta.get_field("rounded_total"):
- if self.doc.is_rounded_total_disabled():
- self.doc.rounded_total = self.doc.base_rounded_total = 0
- return
+ if not self.doc.get('is_consolidated'):
+ if self.doc.meta.get_field("rounded_total"):
+ if self.doc.is_rounded_total_disabled():
+ self.doc.rounded_total = self.doc.base_rounded_total = 0
+ return
- self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
- self.doc.currency, self.doc.precision("rounded_total"))
+ self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
+ self.doc.currency, self.doc.precision("rounded_total"))
- #if print_in_rate is set, we would have already calculated rounding adjustment
- self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
- self.doc.precision("rounding_adjustment"))
+ #if print_in_rate is set, we would have already calculated rounding adjustment
+ self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
+ self.doc.precision("rounding_adjustment"))
- self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
+ self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
def _cleanup(self):
if not self.doc.get('is_consolidated'):
@@ -686,7 +709,7 @@
'mode_of_payment': default_mode_of_payment.mode_of_payment,
'amount': total_amount_to_pay,
'default': 1
- })
+ })
def get_itemised_tax_breakup_html(doc):
if not doc.taxes:
diff --git a/erpnext/controllers/tests/test_item_variant.py b/erpnext/controllers/tests/test_item_variant.py
index 813f0a0..5c6e06a 100644
--- a/erpnext/controllers/tests/test_item_variant.py
+++ b/erpnext/controllers/tests/test_item_variant.py
@@ -1,14 +1,14 @@
-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):
@@ -18,7 +18,7 @@
self.assertEqual(variant.get("quality_inspection_template"), "_Test QC Template")
def create_variant_with_tables(item, args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
qc_name = make_quality_inspection_template()
diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py
index 7a4b2d3..e755876 100644
--- a/erpnext/controllers/tests/test_mapper.py
+++ b/erpnext/controllers/tests/test_mapper.py
@@ -1,12 +1,12 @@
-from __future__ import unicode_literals
+import json
import unittest
-import frappe
-import random, json
+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..49b844b 100644
--- a/erpnext/controllers/tests/test_qty_based_taxes.py
+++ b/erpnext/controllers/tests/test_qty_based_taxes.py
@@ -1,8 +1,9 @@
-from __future__ import unicode_literals, print_function
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/tests/test_transaction_base.py b/erpnext/controllers/tests/test_transaction_base.py
new file mode 100644
index 0000000..13aa697
--- /dev/null
+++ b/erpnext/controllers/tests/test_transaction_base.py
@@ -0,0 +1,22 @@
+import unittest
+
+import frappe
+
+
+class TestUtils(unittest.TestCase):
+ def test_reset_default_field_value(self):
+ doc = frappe.get_doc({
+ "doctype": "Purchase Receipt",
+ "set_warehouse": "Warehouse 1",
+ })
+
+ # Same values
+ doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}]
+ doc.reset_default_field_value("set_warehouse", "items", "warehouse")
+ self.assertEqual(doc.set_warehouse, "Warehouse 1")
+
+ # Mixed values
+ doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}]
+ doc.reset_default_field_value("set_warehouse", "items", "warehouse")
+ self.assertEqual(doc.set_warehouse, None)
+
diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py
index 9b4b0eb..1cb101f 100644
--- a/erpnext/controllers/trends.py
+++ b/erpnext/controllers/trends.py
@@ -1,10 +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
-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..23463ab 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -1,13 +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 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 +21,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 +70,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 +80,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 f7c6b6c..20fb987 100644
--- a/erpnext/crm/doctype/appointment/appointment.py
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -1,18 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-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):
diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py
index c7563e9..f4086dc 100644
--- a/erpnext/crm/doctype/appointment/test_appointment.py
+++ b/erpnext/crm/doctype/appointment/test_appointment.py
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import datetime
+import unittest
import frappe
-import unittest
-import datetime
def create_test_lead():
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
index 27f14b1..1431b03 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 _
-import datetime
from frappe.model.document import Document
@@ -28,10 +28,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..bc68bbd 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..756c849 100644
--- a/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py
+++ b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..4294e6d 100644
--- a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
+++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..de9b5a1 100644
--- a/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
+++ b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 CampaignEmailSchedule(Document):
pass
diff --git a/erpnext/healthcare/__init__.py b/erpnext/crm/doctype/competitor/__init__.py
similarity index 100%
rename from erpnext/healthcare/__init__.py
rename to erpnext/crm/doctype/competitor/__init__.py
diff --git a/erpnext/crm/doctype/competitor/competitor.js b/erpnext/crm/doctype/competitor/competitor.js
new file mode 100644
index 0000000..a5b617d
--- /dev/null
+++ b/erpnext/crm/doctype/competitor/competitor.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('Competitor', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/crm/doctype/competitor/competitor.json b/erpnext/crm/doctype/competitor/competitor.json
new file mode 100644
index 0000000..280441f
--- /dev/null
+++ b/erpnext/crm/doctype/competitor/competitor.json
@@ -0,0 +1,68 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:competitor_name",
+ "creation": "2021-10-21 10:28:52.071316",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "competitor_name",
+ "website"
+ ],
+ "fields": [
+ {
+ "fieldname": "competitor_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Competitor Name",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "website",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Website",
+ "options": "URL"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-10-21 12:43:59.106807",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Competitor",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales User",
+ "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/crm/doctype/competitor/competitor.py b/erpnext/crm/doctype/competitor/competitor.py
new file mode 100644
index 0000000..a292e461
--- /dev/null
+++ b/erpnext/crm/doctype/competitor/competitor.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 Competitor(Document):
+ pass
diff --git a/erpnext/crm/doctype/competitor/test_competitor.py b/erpnext/crm/doctype/competitor/test_competitor.py
new file mode 100644
index 0000000..f77d7e6
--- /dev/null
+++ b/erpnext/crm/doctype/competitor/test_competitor.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestCompetitor(unittest.TestCase):
+ pass
diff --git a/erpnext/healthcare/doctype/therapy_plan_detail/__init__.py b/erpnext/crm/doctype/competitor_detail/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/therapy_plan_detail/__init__.py
rename to erpnext/crm/doctype/competitor_detail/__init__.py
diff --git a/erpnext/crm/doctype/competitor_detail/competitor_detail.json b/erpnext/crm/doctype/competitor_detail/competitor_detail.json
new file mode 100644
index 0000000..9512b22
--- /dev/null
+++ b/erpnext/crm/doctype/competitor_detail/competitor_detail.json
@@ -0,0 +1,33 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2021-10-21 10:34:58.841689",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "competitor"
+ ],
+ "fields": [
+ {
+ "fieldname": "competitor",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Competitor",
+ "options": "Competitor",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-10-21 10:34:58.841689",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Competitor Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/competitor_detail/competitor_detail.py b/erpnext/crm/doctype/competitor_detail/competitor_detail.py
new file mode 100644
index 0000000..0ef7560
--- /dev/null
+++ b/erpnext/crm/doctype/competitor_detail/competitor_detail.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 CompetitorDetail(Document):
+ pass
diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py
index c39397b..e21f46a 100644
--- a/erpnext/crm/doctype/contract/contract.py
+++ b/erpnext/crm/doctype/contract/contract.py
@@ -1,13 +1,11 @@
-# -*- 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.utils import getdate, now_datetime, nowdate
+from frappe.utils import getdate, nowdate
class Contract(Document):
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..e685362 100644
--- a/erpnext/crm/doctype/contract/test_contract.py
+++ b/erpnext/crm/doctype/contract/test_contract.py
@@ -1,13 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
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..4e4e998 100644
--- a/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.py
+++ b/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.py
@@ -1,10 +1,9 @@
-# -*- 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 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..dfcbdfc 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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 9281220..7439e4c 100644
--- a/erpnext/crm/doctype/contract_template/contract_template.py
+++ b/erpnext/crm/doctype/contract_template/contract_template.py
@@ -1,13 +1,13 @@
-# -*- 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 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):
@@ -16,7 +16,7 @@
@frappe.whitelist()
def get_contract_template(template_name, doc):
- if isinstance(doc, string_types):
+ if isinstance(doc, str):
doc = json.loads(doc)
contract_template = frappe.get_doc("Contract Template", template_name)
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..773d81e 100644
--- a/erpnext/crm/doctype/contract_template/test_contract_template.py
+++ b/erpnext/crm/doctype/contract_template/test_contract_template.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..18600d9 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
@@ -1,10 +1,9 @@
-# -*- 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 ContractTemplateFulfilmentTerms(Document):
pass
diff --git a/erpnext/healthcare/doctype/healthcare_settings/__init__.py b/erpnext/crm/doctype/crm_settings/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/healthcare_settings/__init__.py
rename to erpnext/crm/doctype/crm_settings/__init__.py
diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.js b/erpnext/crm/doctype/crm_settings/crm_settings.js
new file mode 100644
index 0000000..c6569d8
--- /dev/null
+++ b/erpnext/crm/doctype/crm_settings/crm_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('CRM Settings', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.json b/erpnext/crm/doctype/crm_settings/crm_settings.json
new file mode 100644
index 0000000..95b19fa
--- /dev/null
+++ b/erpnext/crm/doctype/crm_settings/crm_settings.json
@@ -0,0 +1,114 @@
+{
+ "actions": [],
+ "creation": "2021-09-09 17:03:22.754446",
+ "description": "Settings for Selling Module",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "engine": "InnoDB",
+ "field_order": [
+ "section_break_5",
+ "campaign_naming_by",
+ "allow_lead_duplication_based_on_emails",
+ "column_break_4",
+ "create_event_on_next_contact_date",
+ "auto_creation_of_contact",
+ "opportunity_section",
+ "close_opportunity_after_days",
+ "column_break_9",
+ "create_event_on_next_contact_date_opportunity",
+ "quotation_section",
+ "default_valid_till"
+ ],
+ "fields": [
+ {
+ "fieldname": "campaign_naming_by",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Campaign Naming By",
+ "options": "Campaign Name\nNaming Series"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "default_valid_till",
+ "fieldtype": "Data",
+ "label": "Default Quotation Validity Days"
+ },
+ {
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break",
+ "label": "Lead"
+ },
+ {
+ "default": "0",
+ "fieldname": "allow_lead_duplication_based_on_emails",
+ "fieldtype": "Check",
+ "label": "Allow Lead Duplication based on Emails"
+ },
+ {
+ "default": "1",
+ "fieldname": "auto_creation_of_contact",
+ "fieldtype": "Check",
+ "label": "Auto Creation of Contact"
+ },
+ {
+ "default": "1",
+ "fieldname": "create_event_on_next_contact_date",
+ "fieldtype": "Check",
+ "label": "Create Event on Next Contact Date"
+ },
+ {
+ "fieldname": "opportunity_section",
+ "fieldtype": "Section Break",
+ "label": "Opportunity"
+ },
+ {
+ "default": "15",
+ "description": "Auto close Opportunity Replied after the no. of days mentioned above",
+ "fieldname": "close_opportunity_after_days",
+ "fieldtype": "Int",
+ "label": "Close Replied Opportunity After Days"
+ },
+ {
+ "default": "1",
+ "fieldname": "create_event_on_next_contact_date_opportunity",
+ "fieldtype": "Check",
+ "label": "Create Event on Next Contact Date"
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "quotation_section",
+ "fieldtype": "Section Break",
+ "label": "Quotation"
+ }
+ ],
+ "icon": "fa fa-cog",
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "migration_hash": "3ae78b12dd1c64d551736c6e82092f90",
+ "modified": "2021-11-03 09:00:36.883496",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "CRM Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 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/crm/doctype/crm_settings/crm_settings.py b/erpnext/crm/doctype/crm_settings/crm_settings.py
new file mode 100644
index 0000000..bde5254
--- /dev/null
+++ b/erpnext/crm/doctype/crm_settings/crm_settings.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 CRMSettings(Document):
+ pass
diff --git a/erpnext/crm/doctype/crm_settings/test_crm_settings.py b/erpnext/crm/doctype/crm_settings/test_crm_settings.py
new file mode 100644
index 0000000..3372c5d
--- /dev/null
+++ b/erpnext/crm/doctype/crm_settings/test_crm_settings.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestCRMSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
index 71c93e8..d444432 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..997d903 100644
--- a/erpnext/crm/doctype/email_campaign/test_email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/test_email_campaign.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 cad17a3..9adbe8b 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -1,16 +1,26 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
-from erpnext.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,
+ get_link_to_form,
+ 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):
@@ -63,6 +73,7 @@
def on_update(self):
self.add_calendar_event()
+ self.update_prospects()
def before_insert(self):
self.contact_doc = self.create_contact()
@@ -81,23 +92,31 @@
self.contact_doc.save()
def add_calendar_event(self, opts=None, force=False):
- super(Lead, self).add_calendar_event({
- "owner": self.lead_owner,
- "starts_on": self.contact_date,
- "ends_on": self.ends_on or "",
- "subject": ('Contact ' + cstr(self.lead_name)),
- "description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
- }, force)
+ if frappe.db.get_single_value('CRM Settings', 'create_event_on_next_contact_date'):
+ super(Lead, self).add_calendar_event({
+ "owner": self.lead_owner,
+ "starts_on": self.contact_date,
+ "ends_on": self.ends_on or "",
+ "subject": ('Contact ' + cstr(self.lead_name)),
+ "description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
+ }, force)
+
+ def update_prospects(self):
+ prospects = frappe.get_all('Prospect Lead', filters={'lead': self.name}, fields=['parent'])
+ 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
- duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]})
- duplicate_leads = [lead.name for lead in duplicate_leads]
+ if not frappe.db.get_single_value('CRM Settings', 'allow_lead_duplication_based_on_emails'):
+ duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]})
+ duplicate_leads = [frappe.bold(get_link_to_form('Lead', lead.name)) for lead in duplicate_leads]
- if duplicate_leads:
- frappe.throw(_("Email Address must be unique, already exists for {0}")
- .format(comma_and(duplicate_leads)), frappe.DuplicateEntryError)
+ if duplicate_leads:
+ frappe.throw(_("Email Address must be unique, already exists for {0}")
+ .format(comma_and(duplicate_leads)), frappe.DuplicateEntryError)
def on_trash(self):
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name)
@@ -156,40 +175,42 @@
self.title = self.company_name or self.lead_name
def create_contact(self):
- if not self.lead_name:
- self.set_full_name()
- self.set_lead_name()
+ if frappe.db.get_single_value('CRM Settings', 'auto_creation_of_contact'):
+ if not self.lead_name:
+ self.set_full_name()
+ self.set_lead_name()
- contact = frappe.new_doc("Contact")
- contact.update({
- "first_name": self.first_name or self.lead_name,
- "last_name": self.last_name,
- "salutation": self.salutation,
- "gender": self.gender,
- "designation": self.designation,
- })
-
- if self.email_id:
- contact.append("email_ids", {
- "email_id": self.email_id,
- "is_primary": 1
+ contact = frappe.new_doc("Contact")
+ contact.update({
+ "first_name": self.first_name or self.lead_name,
+ "last_name": self.last_name,
+ "salutation": self.salutation,
+ "gender": self.gender,
+ "designation": self.designation,
+ "company_name": self.company_name,
})
- if self.phone:
- contact.append("phone_nos", {
- "phone": self.phone,
- "is_primary_phone": 1
- })
+ if self.email_id:
+ contact.append("email_ids", {
+ "email_id": self.email_id,
+ "is_primary": 1
+ })
- if self.mobile_no:
- contact.append("phone_nos", {
- "phone": self.mobile_no,
- "is_primary_mobile_no":1
- })
+ if self.phone:
+ contact.append("phone_nos", {
+ "phone": self.phone,
+ "is_primary_phone": 1
+ })
- contact.insert(ignore_permissions=True)
+ if self.mobile_no:
+ contact.append("phone_nos", {
+ "phone": self.mobile_no,
+ "is_primary_mobile_no":1
+ })
- return contact
+ contact.insert(ignore_permissions=True)
+
+ return contact
@frappe.whitelist()
def make_customer(source_name, target_doc=None):
@@ -354,3 +375,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 3950d06..017390d 100644
--- a/erpnext/crm/doctype/lead/lead_dashboard.py
+++ b/erpnext/crm/doctype/lead/lead_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'lead',
@@ -13,7 +10,7 @@
},
'transactions': [
{
- 'items': ['Opportunity', 'Quotation']
+ 'items': ['Opportunity', 'Quotation', 'Prospect']
},
]
}
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 d7bc461..56bfc8f 100644
--- a/erpnext/crm/doctype/lead/test_lead.py
+++ b/erpnext/crm/doctype/lead/test_lead.py
@@ -1,11 +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 unittest
import frappe
from frappe.utils import random_string
-import unittest
test_records = frappe.get_test_records('Lead')
diff --git a/erpnext/crm/doctype/lead_source/lead_source.py b/erpnext/crm/doctype/lead_source/lead_source.py
index 5c64fb8..d9e0028 100644
--- a/erpnext/crm/doctype/lead_source/lead_source.py
+++ b/erpnext/crm/doctype/lead_source/lead_source.py
@@ -1,10 +1,10 @@
-# -*- 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 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..1363d1f 100644
--- a/erpnext/crm/doctype/lead_source/test_lead_source.py
+++ b/erpnext/crm/doctype/lead_source/test_lead_source.py
@@ -1,10 +1,9 @@
-# -*- 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 TestLeadSource(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
index 9b88d78..8fd4978 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
@@ -1,16 +1,16 @@
-# -*- 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 requests
from frappe import _
-from frappe.utils import get_url_to_form
from frappe.model.document import Document
+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):
@@ -146,7 +146,7 @@
except Exception as e:
self.api_error(response)
-
+
return response
def get_headers(self):
@@ -168,7 +168,7 @@
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)
@@ -176,7 +176,7 @@
response = requests.get(url=url, headers=self.get_headers())
if response.status_code !=200:
raise
-
+
except Exception:
self.api_error(response)
diff --git a/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py
index 9c3ef3f..09732e4 100644
--- a/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py
@@ -1,10 +1,9 @@
-# -*- 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 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..51e4d5c 100644
--- a/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.py
+++ b/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.py
@@ -1,9 +1,9 @@
-# -*- 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 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..766be85 100644
--- a/erpnext/crm/doctype/market_segment/market_segment.py
+++ b/erpnext/crm/doctype/market_segment/market_segment.py
@@ -1,10 +1,9 @@
-# -*- 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 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..20b73b1 100644
--- a/erpnext/crm/doctype/market_segment/test_market_segment.py
+++ b/erpnext/crm/doctype/market_segment/test_market_segment.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestMarketSegment(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index cb95881..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"]],
}
}
});
@@ -132,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) {
@@ -152,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
@@ -169,6 +258,7 @@
}
this.setup_queries();
+ this.frm.trigger('currency');
}
setup_queries() {
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index 4ba4140..feb6044 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -23,7 +23,6 @@
"status",
"converted_by",
"sales_stage",
- "order_lost_reason",
"first_response_time",
"expected_closing",
"next_contact",
@@ -33,12 +32,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",
@@ -56,7 +63,11 @@
"transaction_date",
"language",
"amended_from",
- "lost_reasons"
+ "lost_detail_section",
+ "lost_reasons",
+ "order_lost_reason",
+ "column_break_56",
+ "competitors"
],
"fields": [
{
@@ -146,10 +157,9 @@
"reqd": 1
},
{
- "depends_on": "eval:doc.status===\"Lost\"",
"fieldname": "order_lost_reason",
"fieldtype": "Small Text",
- "label": "Lost Reason",
+ "label": "Detailed Reason",
"no_copy": 1,
"read_only": 1
},
@@ -401,6 +411,7 @@
"width": "150px"
},
{
+ "depends_on": "eval:doc.status===\"Lost\"",
"fieldname": "lost_reasons",
"fieldtype": "Table MultiSelect",
"label": "Lost Reasons",
@@ -425,15 +436,86 @@
"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
+ },
+ {
+ "fieldname": "lost_detail_section",
+ "fieldtype": "Section Break",
+ "label": "Lost Reasons"
+ },
+ {
+ "fieldname": "column_break_56",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "competitors",
+ "fieldtype": "Table MultiSelect",
+ "label": "Competitors",
+ "options": "Competitor Detail",
+ "read_only": 1
}
],
"icon": "fa fa-info-sign",
"idx": 195,
"links": [],
- "modified": "2021-06-04 10:11:22.831139",
+ "migration_hash": "d87c646ea2579b6900197fd41e6c5c5a",
+ "modified": "2021-10-21 11:04:30.151379",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index a74a94a..fcbd4de 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -1,15 +1,19 @@
# 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 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.query_builder import DocType
+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):
@@ -25,10 +29,10 @@
})
self.make_new_lead_if_required()
-
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_cust_name()
+ self.map_fields()
if not self.title:
self.title = self.customer_name
@@ -36,25 +40,51 @@
if not self.with_items:
self.items = []
+ else:
+ self.calculate_totals()
+
+ def map_fields(self):
+ for field in self.meta.fields:
+ if not self.get(field.fieldname):
+ try:
+ value = frappe.db.get_value(self.opportunity_from, self.party_name, field.fieldname)
+ frappe.db.set(self, field.fieldname, value)
+ except Exception:
+ continue
+
+ def calculate_totals(self):
+ total = base_total = 0
+ for item in self.get('items'):
+ 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:
# check if customer is already created agains the self.contact_email
- customer = frappe.db.sql("""select
- distinct `tabDynamic Link`.link_name as customer
- from
- `tabContact`,
- `tabDynamic Link`
- where `tabContact`.email_id='{0}'
- and
- `tabContact`.name=`tabDynamic Link`.parent
- and
- ifnull(`tabDynamic Link`.link_name, '')<>''
- and
- `tabDynamic Link`.link_doctype='Customer'
- """.format(self.contact_email), as_dict=True)
- if customer and customer[0].customer:
- self.party_name = customer[0].customer
+ dynamic_link, contact = DocType("Dynamic Link"), DocType("Contact")
+ customer = frappe.qb.from_(
+ dynamic_link
+ ).join(
+ contact
+ ).on(
+ (contact.name == dynamic_link.parent)
+ & (dynamic_link.link_doctype == "Customer")
+ & (contact.email_id == self.contact_email)
+ ).select(
+ dynamic_link.link_name
+ ).distinct().run(as_dict=True)
+
+ if customer and customer[0].link_name:
+ self.party_name = customer[0].link_name
self.opportunity_from = "Customer"
return
@@ -86,16 +116,20 @@
self.party_name = lead_name
@frappe.whitelist()
- def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
+ def declare_enquiry_lost(self, lost_reasons_list, competitors, detailed_reason=None):
if not self.has_active_quotation():
- frappe.db.set(self, 'status', 'Lost')
+ self.status = 'Lost'
+ self.lost_reasons = self.competitors = []
if detailed_reason:
- frappe.db.set(self, 'order_lost_reason', detailed_reason)
+ self.order_lost_reason = detailed_reason
for reason in lost_reasons_list:
self.append('lost_reasons', reason)
+ for competitor in competitors:
+ self.append('competitors', competitor)
+
self.save()
else:
@@ -157,30 +191,31 @@
self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False):
- if not opts:
- opts = frappe._dict()
+ if frappe.db.get_single_value('CRM Settings', 'create_event_on_next_contact_date_opportunity'):
+ if not opts:
+ opts = frappe._dict()
- opts.description = ""
- opts.contact_date = self.contact_date
+ opts.description = ""
+ opts.contact_date = self.contact_date
- if self.party_name and self.opportunity_from == 'Customer':
- if self.contact_person:
- opts.description = 'Contact '+cstr(self.contact_person)
- else:
- opts.description = 'Contact customer '+cstr(self.party_name)
- elif self.party_name and self.opportunity_from == 'Lead':
- if self.contact_display:
- opts.description = 'Contact '+cstr(self.contact_display)
- else:
- opts.description = 'Contact lead '+cstr(self.party_name)
+ if self.party_name and self.opportunity_from == 'Customer':
+ if self.contact_person:
+ opts.description = 'Contact '+cstr(self.contact_person)
+ else:
+ opts.description = 'Contact customer '+cstr(self.party_name)
+ elif self.party_name and self.opportunity_from == 'Lead':
+ if self.contact_display:
+ opts.description = 'Contact '+cstr(self.contact_display)
+ else:
+ opts.description = 'Contact lead '+cstr(self.party_name)
- opts.subject = opts.description
- opts.description += '. By : ' + cstr(self.contact_by)
+ opts.subject = opts.description
+ opts.description += '. By : ' + cstr(self.contact_by)
- if self.to_discuss:
- opts.description += ' To Discuss : ' + cstr(self.to_discuss)
+ if self.to_discuss:
+ opts.description += ' To Discuss : ' + cstr(self.to_discuss)
- super(Opportunity, self).add_calendar_event(opts, force)
+ super(Opportunity, self).add_calendar_event(opts, force)
def validate_item_details(self):
if not self.get('items'):
@@ -219,13 +254,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 +277,7 @@
"doctype": "Quotation",
"field_map": {
"opportunity_from": "quotation_to",
- "name": "enq_no",
+ "name": "enq_no"
}
},
"Opportunity Item": {
@@ -290,6 +318,8 @@
@frappe.whitelist()
def make_customer(source_name, target_doc=None):
def set_missing_values(source, target):
+ target.opportunity_name = source.name
+
if source.opportunity_from == "Lead":
target.lead_name = source.party_name
@@ -334,7 +364,7 @@
def auto_close_opportunity():
""" auto close the `Replied` Opportunities after 7 days """
- auto_close_after_days = frappe.db.get_single_value("Selling Settings", "close_opportunity_after_days") or 15
+ auto_close_after_days = frappe.db.get_single_value("CRM Settings", "close_opportunity_after_days") or 15
opportunities = frappe.db.sql(""" select name from tabOpportunity where status='Replied' and
modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """, (auto_close_after_days), as_dict=True)
diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
index b8c53f0..708fb12 100644
--- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
+++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'opportunity',
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py
index 52aa0b0..6e6fed5 100644
--- a/erpnext/crm/doctype/opportunity/test_opportunity.py
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.py
@@ -1,12 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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 +62,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 +74,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,6 +89,7 @@
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"
})
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..4d28587 100644
--- a/erpnext/crm/doctype/opportunity_item/opportunity_item.py
+++ b/erpnext/crm/doctype/opportunity_item/opportunity_item.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..84a9a52 100644
--- a/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.py
+++ b/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.py
@@ -1,9 +1,9 @@
-# -*- 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 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..d572185 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..1bb31ec 100644
--- a/erpnext/crm/doctype/opportunity_type/opportunity_type.py
+++ b/erpnext/crm/doctype/opportunity_type/opportunity_type.py
@@ -1,10 +1,9 @@
-# -*- 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.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..ae0d782 100644
--- a/erpnext/crm/doctype/opportunity_type/test_opportunity_type.py
+++ b/erpnext/crm/doctype/opportunity_type/test_opportunity_type.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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%
rename from erpnext/healthcare/report/__init__.py
rename 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..c9554ba
--- /dev/null
+++ b/erpnext/crm/doctype/prospect/prospect.json
@@ -0,0 +1,212 @@
+{
+ "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",
+ "company",
+ "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"
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-11-01 13:10:36.759249",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Prospect",
+ "naming_rule": "By fieldname",
+ "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..d2099ed 100644
--- a/erpnext/crm/doctype/sales_stage/sales_stage.py
+++ b/erpnext/crm/doctype/sales_stage/sales_stage.py
@@ -1,10 +1,9 @@
-# -*- 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 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..d088f96 100644
--- a/erpnext/crm/doctype/sales_stage/test_sales_stage.py
+++ b/erpnext/crm/doctype/sales_stage/test_sales_stage.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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 a8f5dee..6874caa 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.js
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.js
@@ -80,10 +80,10 @@
refresh: function(frm) {
frm.trigger('text');
-
+
if (frm.doc.docstatus === 1) {
if (!['Posted', 'Deleted'].includes(frm.doc.post_status)) {
- frm.trigger('add_post_btn');
+ frm.trigger('add_post_btn');
}
if (frm.doc.post_status !='Deleted') {
frm.add_custom_button(('Delete Post'), function() {
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 95320bf..3f63c1d 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.py
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-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):
@@ -26,7 +27,7 @@
if self.scheduled_time:
self.post_status = "Scheduled"
super(SocialMediaPost, self).submit()
-
+
def on_cancel(self):
self.db_set('post_status', 'Cancelled')
@@ -35,11 +36,11 @@
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()
@@ -51,7 +52,7 @@
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()
@@ -67,7 +68,7 @@
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)
frappe.log_error(message=frappe.get_traceback(), title=title)
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..7574476 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
@@ -1,10 +1,9 @@
-# -*- 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 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..9dbce8f 100644
--- a/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py
@@ -1,10 +1,9 @@
-# -*- 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 TestTwitterSettings(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
index 4775656..be7d914 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
@@ -1,15 +1,18 @@
-# -*- 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, 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):
@@ -53,10 +56,10 @@
frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
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(self.access_token, self.access_token_secret)
+ # 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(self.access_token, self.access_token_secret)
return tweepy.API(auth)
@@ -90,20 +93,20 @@
def delete_tweet(self, tweet_id):
api = self.get_api()
- try:
+ 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:
+ 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]
diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py
index f244daf..9b56170 100644
--- a/erpnext/crm/doctype/utils.py
+++ b/erpnext/crm/doctype/utils.py
@@ -1,6 +1,5 @@
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.py b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
index 238884b..6f3e311 100644
--- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
+++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
@@ -1,11 +1,12 @@
# 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 flt
+
def execute(filters=None):
columns, data = [], []
columns=get_columns("Campaign Name")
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..ed6cefb 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
@@ -1,9 +1,10 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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.py b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
index e66bc1e..1f43fa0 100644
--- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
+++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
@@ -1,11 +1,12 @@
# 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 _, 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.py b/erpnext/crm/report/lead_details/lead_details.py
index 072a476..09eba7c 100644
--- a/erpnext/crm/report/lead_details/lead_details.py
+++ b/erpnext/crm/report/lead_details/lead_details.py
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# 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)
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..2932211 100644
--- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py
+++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py
@@ -1,11 +1,12 @@
# 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 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.py b/erpnext/crm/report/lost_opportunity/lost_opportunity.py
index 858dcc4..60d4be8 100644
--- a/erpnext/crm/report/lost_opportunity/lost_opportunity.py
+++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.py
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# 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)
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..f53b5bd
--- /dev/null
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py
@@ -0,0 +1,253 @@
+# 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 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 self.formatted_data.items():
+ row_based_on={
+ 'Opportunity Owner': 'opportunity_owner',
+ 'Source': 'source',
+ 'Opportunity Type': 'opportunity_type'
+ }[self.filters.get('based_on')]
+
+ row = {row_based_on: based_on}
+
+ for d in self.query_result:
+ sales_stage = d.get('sales_stage')
+ 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
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 425b7a8..41cb442 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
@@ -1,11 +1,12 @@
# 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 add_days, now
+
def execute(filters=None):
columns, data = [], []
set_defaut_value_for_filters(filters)
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..1c7846b
--- /dev/null
+++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
@@ -0,0 +1,332 @@
+# 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 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 self.periodic_data.items():
+ 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
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..5a63dc1 100644
--- a/erpnext/crm/workspace/crm/crm.json
+++ b/erpnext/crm/workspace/crm/crm.json
@@ -1,5 +1,4 @@
{
- "category": "",
"charts": [
{
"chart_name": "Territory Wise Sales"
@@ -7,18 +6,12 @@
],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"CRM\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Lead\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Opportunity\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Customer\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Sales Pipeline\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Campaign\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
"creation": "2020-01-23 14:48:30.183272",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "crm",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "CRM",
"links": [
{
@@ -148,6 +141,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,15 +414,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:56.913091",
+ "modified": "2021-08-20 12:15:56.913092",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM",
- "onboarding": "CRM",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/demo/data/drug_list.json b/erpnext/demo/data/drug_list.json
index e91c30d..3069042 100644
--- a/erpnext/demo/data/drug_list.json
+++ b/erpnext/demo/data/drug_list.json
@@ -60,7 +60,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -144,7 +143,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -226,7 +224,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -308,7 +305,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -390,7 +386,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -472,7 +467,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -554,7 +548,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -636,7 +629,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -718,7 +710,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -800,7 +791,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -882,7 +872,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -964,7 +953,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1046,7 +1034,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1128,7 +1115,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1210,7 +1196,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1292,7 +1277,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1374,7 +1358,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1456,7 +1439,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1538,7 +1520,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1620,7 +1601,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1702,7 +1682,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1784,7 +1763,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1866,7 +1844,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -1948,7 +1925,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2030,7 +2006,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2112,7 +2087,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2194,7 +2168,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2276,7 +2249,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2358,7 +2330,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2440,7 +2411,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2522,7 +2492,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2604,7 +2573,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2686,7 +2654,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2768,7 +2735,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2850,7 +2816,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -2932,7 +2897,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3014,7 +2978,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3098,7 +3061,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3180,7 +3142,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3262,7 +3223,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3344,7 +3304,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3426,7 +3385,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3508,7 +3466,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3590,7 +3547,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3672,7 +3628,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3754,7 +3709,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3836,7 +3790,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -3918,7 +3871,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4000,7 +3952,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4082,7 +4033,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4164,7 +4114,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4246,7 +4195,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4328,7 +4276,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4410,7 +4357,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4492,7 +4438,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4574,7 +4519,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4656,7 +4600,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4738,7 +4681,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4820,7 +4762,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4902,7 +4843,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -4984,7 +4924,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -5066,7 +5005,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
@@ -5148,7 +5086,6 @@
"standard_rate": 0.0,
"stock_uom": "Nos",
"supplier_items": [],
- "synced_with_hub": 0,
"taxes": [],
"thumbnail": null,
"tolerance": 0.0,
diff --git a/erpnext/demo/demo.py b/erpnext/demo/demo.py
index e89b689..4a18a99 100644
--- a/erpnext/demo/demo.py
+++ b/erpnext/demo/demo.py
@@ -1,11 +1,14 @@
-from __future__ import unicode_literals
+import sys
-import frappe, sys
-import erpnext
+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 +36,6 @@
retail.setup_data()
elif domain== 'Education':
education.setup_data()
- elif domain== 'Healthcare':
- healthcare.setup_data()
site = frappe.local.site
frappe.destroy()
@@ -88,7 +89,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 b1db7b5..5fa181d 100644
--- a/erpnext/demo/domains.py
+++ b/erpnext/demo/domains.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'Manufacturing': {
'company_name': 'Wind Power LLC'
@@ -16,9 +14,6 @@
'Education': {
'company_name': 'Whitmore College'
},
- 'Healthcare': {
- 'company_name': 'ABC Hospital Ltd.'
- },
'Agriculture': {
'company_name': 'Schrute Farms'
},
diff --git a/erpnext/demo/setup/education.py b/erpnext/demo/setup/education.py
index cf9451d..eb833f4 100644
--- a/erpnext/demo/setup/education.py
+++ b/erpnext/demo/setup/education.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, 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..fe1a1fb 100644
--- a/erpnext/demo/setup/manufacture.py
+++ b/erpnext/demo/setup/manufacture.py
@@ -1,12 +1,12 @@
-from __future__ import unicode_literals
+import json
+import random
-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
+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")
@@ -128,7 +128,7 @@
}
for price_list in ("standard_buying", "standard_selling"):
- for item, rate in iteritems(locals().get(price_list)):
+ for item, rate in locals().get(price_list).items():
frappe.get_doc({
"doctype": "Item Price",
"price_list": price_list.replace("_", " ").title(),
diff --git a/erpnext/demo/setup/retail.py b/erpnext/demo/setup/retail.py
index 82d1c15..0469264 100644
--- a/erpnext/demo/setup/retail.py
+++ b/erpnext/demo/setup/retail.py
@@ -1,12 +1,9 @@
-from __future__ import unicode_literals
+import json
-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
-from six import iteritems
def setup_data():
setup_item()
@@ -53,7 +50,7 @@
}
for price_list in ("standard_buying", "standard_selling"):
- for item, rate in iteritems(locals().get(price_list)):
+ for item, rate in locals().get(price_list).items():
frappe.get_doc({
"doctype": "Item Price",
"price_list": price_list.replace("_", " ").title(),
diff --git a/erpnext/demo/setup/setup_data.py b/erpnext/demo/setup/setup_data.py
index 05ee28a..7137c6e 100644
--- a/erpnext/demo/setup/setup_data.py
+++ b/erpnext/demo/setup/setup_data.py
@@ -1,13 +1,16 @@
-from __future__ import print_function, unicode_literals
+import json
+import random
-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 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 +194,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..273a3f9 100644
--- a/erpnext/demo/user/accounts.py
+++ b/erpnext/demo/user/accounts.py
@@ -1,22 +1,26 @@
-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
+import random
+
+import frappe
+from frappe.desk import query_report
+from frappe.utils import random_string
+from frappe.utils.make_random import get_random
import erpnext
-import frappe
-import random
-from frappe.utils import random_string
-from frappe.desk import query_report
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 883a6d8..47519c1 100644
--- a/erpnext/demo/user/education.py
+++ b/erpnext/demo/user/education.py
@@ -1,16 +1,22 @@
-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
+import random
+from datetime import timedelta
import frappe
-import random
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():
diff --git a/erpnext/demo/user/fixed_asset.py b/erpnext/demo/user/fixed_asset.py
index dc094e1..72cd420 100644
--- a/erpnext/demo/user/fixed_asset.py
+++ b/erpnext/demo/user/fixed_asset.py
@@ -1,11 +1,10 @@
-
# 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.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..f84a853 100644
--- a/erpnext/demo/user/hr.py
+++ b/erpnext/demo/user/hr.py
@@ -1,15 +1,21 @@
-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..6b61776 100644
--- a/erpnext/demo/user/manufacturing.py
+++ b/erpnext/demo/user/manufacturing.py
@@ -1,23 +1,24 @@
# 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, 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 +69,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..1203be4 100644
--- a/erpnext/demo/user/projects.py
+++ b/erpnext/demo/user/projects.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, 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..61f081c 100644
--- a/erpnext/demo/user/purchase.py
+++ b/erpnext/demo/user/purchase.py
@@ -1,17 +1,23 @@
# 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, 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..ef6e4c4 100644
--- a/erpnext/demo/user/sales.py
+++ b/erpnext/demo/user/sales.py
@@ -1,14 +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, 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..de37975 100644
--- a/erpnext/demo/user/stock.py
+++ b/erpnext/demo/user/stock.py
@@ -1,15 +1,19 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-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 +70,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 +94,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 9212d2e..e5414a9 100644
--- a/erpnext/domains/agriculture.py
+++ b/erpnext/domains/agriculture.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'Agriculture Task',
diff --git a/erpnext/domains/distribution.py b/erpnext/domains/distribution.py
index 3661260..020ab3b 100644
--- a/erpnext/domains/distribution.py
+++ b/erpnext/domains/distribution.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'Item',
diff --git a/erpnext/domains/education.py b/erpnext/domains/education.py
index 870624a..11ea9b4 100644
--- a/erpnext/domains/education.py
+++ b/erpnext/domains/education.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'Student',
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/hospitality.py b/erpnext/domains/hospitality.py
index 2a2d0c6..09b98c2 100644
--- a/erpnext/domains/hospitality.py
+++ b/erpnext/domains/hospitality.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'Restaurant',
diff --git a/erpnext/domains/manufacturing.py b/erpnext/domains/manufacturing.py
index b9ad49e..96ce194 100644
--- a/erpnext/domains/manufacturing.py
+++ b/erpnext/domains/manufacturing.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'Item',
diff --git a/erpnext/domains/non_profit.py b/erpnext/domains/non_profit.py
index 7c4f6b1..d9fc5e5 100644
--- a/erpnext/domains/non_profit.py
+++ b/erpnext/domains/non_profit.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'Non Profit',
diff --git a/erpnext/domains/retail.py b/erpnext/domains/retail.py
index 7360761..07b2e27 100644
--- a/erpnext/domains/retail.py
+++ b/erpnext/domains/retail.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'POS',
diff --git a/erpnext/domains/services.py b/erpnext/domains/services.py
index 8921372..6035afb 100644
--- a/erpnext/domains/services.py
+++ b/erpnext/domains/services.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
data = {
'desktop_icons': [
'Project',
diff --git a/erpnext/education/__init__.py b/erpnext/education/__init__.py
index c0589bb..56c2b29 100644
--- a/erpnext/education/__init__.py
+++ b/erpnext/education/__init__.py
@@ -1,7 +1,7 @@
-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..d9013b0 100644
--- a/erpnext/education/api.py
+++ b/erpnext/education/api.py
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies 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.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
@@ -124,7 +125,7 @@
:param student: Student.
"""
- guardians = frappe.get_list("Student Guardian", fields=["guardian"] ,
+ guardians = frappe.get_all("Student Guardian", fields=["guardian"] ,
filters={"parent": student})
return guardians
@@ -136,10 +137,10 @@
:param student_group: Student Group.
"""
if include_inactive:
- students = frappe.get_list("Student Group Student", fields=["student", "student_name"] ,
+ students = frappe.get_all("Student Group Student", fields=["student", "student_name"] ,
filters={"parent": student_group}, order_by= "group_roll_number")
else:
- students = frappe.get_list("Student Group Student", fields=["student", "student_name"] ,
+ students = frappe.get_all("Student Group Student", fields=["student", "student_name"] ,
filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
return students
@@ -163,7 +164,7 @@
:param fee_structure: Fee Structure.
"""
if fee_structure:
- fs = frappe.get_list("Fee Component", fields=["fees_category", "description", "amount"] , filters={"parent": fee_structure}, order_by= "idx")
+ fs = frappe.get_all("Fee Component", fields=["fees_category", "description", "amount"] , filters={"parent": fee_structure}, order_by= "idx")
return fs
@@ -174,7 +175,7 @@
:param program: Program.
:param student_category: Student Category
"""
- fs = frappe.get_list("Program Fee", fields=["academic_term", "fee_structure", "due_date", "amount"] ,
+ fs = frappe.get_all("Program Fee", fields=["academic_term", "fee_structure", "due_date", "amount"] ,
filters={"parent": program, "student_category": student_category }, order_by= "idx")
return fs
@@ -219,7 +220,7 @@
:param Course: Course
"""
- return frappe.get_list("Course Assessment Criteria", \
+ return frappe.get_all("Course Assessment Criteria",
fields=["assessment_criteria", "weightage"], filters={"parent": course}, order_by= "idx")
@@ -252,7 +253,7 @@
:param Assessment Plan: Assessment Plan
"""
- return frappe.get_list("Assessment Plan Criteria", \
+ return frappe.get_all("Assessment Plan Criteria",
fields=["assessment_criteria", "maximum_score", "docstatus"], filters={"parent": assessment_plan}, order_by= "idx")
diff --git a/erpnext/education/doctype/academic_term/academic_term.py b/erpnext/education/doctype/academic_term/academic_term.py
index fa7f289..93861ca 100644
--- a/erpnext/education/doctype/academic_term/academic_term.py
+++ b/erpnext/education/doctype/academic_term/academic_term.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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):
diff --git a/erpnext/education/doctype/academic_term/academic_term_dashboard.py b/erpnext/education/doctype/academic_term/academic_term_dashboard.py
index eb2f907..c686b09 100644
--- a/erpnext/education/doctype/academic_term/academic_term_dashboard.py
+++ b/erpnext/education/doctype/academic_term/academic_term_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'academic_term',
diff --git a/erpnext/education/doctype/academic_term/test_academic_term.py b/erpnext/education/doctype/academic_term/test_academic_term.py
index 0964a56..0e39fb0 100644
--- a/erpnext/education/doctype/academic_term/test_academic_term.py
+++ b/erpnext/education/doctype/academic_term/test_academic_term.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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.py b/erpnext/education/doctype/academic_year/academic_year.py
index f2858a4..e2010fb 100644
--- a/erpnext/education/doctype/academic_year/academic_year.py
+++ b/erpnext/education/doctype/academic_year/academic_year.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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 d3734df..ede2411 100644
--- a/erpnext/education/doctype/academic_year/academic_year_dashboard.py
+++ b/erpnext/education/doctype/academic_year/academic_year_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'academic_year',
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 51e9cf3..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()
- ]);
-});
diff --git a/erpnext/education/doctype/academic_year/test_academic_year.py b/erpnext/education/doctype/academic_year/test_academic_year.py
index 9da75a7..6d33fe6 100644
--- a/erpnext/education/doctype/academic_year/test_academic_year.py
+++ b/erpnext/education/doctype/academic_year/test_academic_year.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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.py b/erpnext/education/doctype/article/article.py
index b5cc5cb..8f1a2e3 100644
--- a/erpnext/education/doctype/article/article.py
+++ b/erpnext/education/doctype/article/article.py
@@ -1,11 +1,11 @@
-# -*- 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 Article(Document):
def get_article(self):
pass
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..2ea5c82 100644
--- a/erpnext/education/doctype/article/test_article.py
+++ b/erpnext/education/doctype/article/test_article.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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 bfbf26c..58448ea 100644
--- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
+++ b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
@@ -1,8 +1,7 @@
-# -*- 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.model.document import Document
diff --git a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
index fc0d745..40ba0e7 100644
--- a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
+++ b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..d284db5 100644
--- a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py
+++ b/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class AssessmentCriteriaGroup(Document):
pass
diff --git a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.py b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.py
index 5b29337..ccf82ba 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
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..d606ffb 100644
--- a/erpnext/education/doctype/assessment_group/assessment_group.py
+++ b/erpnext/education/doctype/assessment_group/assessment_group.py
@@ -1,10 +1,9 @@
-# -*- 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.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 1a23606..9568091 100644
--- a/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py
+++ b/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py
@@ -1,8 +1,9 @@
# 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',
diff --git a/erpnext/education/doctype/assessment_group/test_assessment_group.py b/erpnext/education/doctype/assessment_group/test_assessment_group.py
index 2fd98b6..6e840aa 100644
--- a/erpnext/education/doctype/assessment_group/test_assessment_group.py
+++ b/erpnext/education/doctype/assessment_group/test_assessment_group.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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.py b/erpnext/education/doctype/assessment_plan/assessment_plan.py
index 16136c1..82a28de 100644
--- a/erpnext/education/doctype/assessment_plan/assessment_plan.py
+++ b/erpnext/education/doctype/assessment_plan/assessment_plan.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# 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 8ac3faf..31b9509 100644
--- a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py
+++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py
@@ -1,8 +1,9 @@
# 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',
diff --git a/erpnext/education/doctype/assessment_plan/test_assessment_plan.py b/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
index 2de4f23..9f55a78 100644
--- a/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
+++ b/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..2cd17d6 100644
--- a/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py
+++ b/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class AssessmentPlanCriteria(Document):
pass
diff --git a/erpnext/education/doctype/assessment_result/assessment_result.py b/erpnext/education/doctype/assessment_result/assessment_result.py
index 7dfe0cf..8278b9e 100644
--- a/erpnext/education/doctype/assessment_result/assessment_result.py
+++ b/erpnext/education/doctype/assessment_result/assessment_result.py
@@ -1,16 +1,16 @@
-# -*- 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
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):
diff --git a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py
index 2526076..3b07417 100644
--- a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py
+++ b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py
@@ -1,8 +1,9 @@
# 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': [
diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.py b/erpnext/education/doctype/assessment_result/test_assessment_result.py
index adce577..c0872df 100644
--- a/erpnext/education/doctype/assessment_result/test_assessment_result.py
+++ b/erpnext/education/doctype/assessment_result/test_assessment_result.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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')
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..5ef1129 100644
--- a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.py
+++ b/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.py
@@ -1,10 +1,9 @@
-# -*- 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.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 a0d286c..4b953be 100644
--- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py
+++ b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class AssessmentResultTool(Document):
pass
diff --git a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.py b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.py
index f784ccb..49e0be0 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..f30cb87 100644
--- a/erpnext/education/doctype/content_activity/content_activity.py
+++ b/erpnext/education/doctype/content_activity/content_activity.py
@@ -1,10 +1,9 @@
-# -*- 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 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..f52f0c8 100644
--- a/erpnext/education/doctype/content_question/content_question.py
+++ b/erpnext/education/doctype/content_question/content_question.py
@@ -1,10 +1,9 @@
-# -*- 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 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..63a5a96 100644
--- a/erpnext/education/doctype/content_question/test_content_question.py
+++ b/erpnext/education/doctype/content_question/test_content_question.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestContentQuestion(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py
index 92f92ed..2d4f282 100644
--- a/erpnext/education/doctype/course/course.py
+++ b/erpnext/education/doctype/course/course.py
@@ -1,12 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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):
diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py
index 8de91b1..276830f 100644
--- a/erpnext/education/doctype/course/course_dashboard.py
+++ b/erpnext/education/doctype/course/course_dashboard.py
@@ -1,8 +1,9 @@
# 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',
diff --git a/erpnext/education/doctype/course/test_course.py b/erpnext/education/doctype/course/test_course.py
index 4667ac4..6381cdb 100644
--- a/erpnext/education/doctype/course/test_course.py
+++ b/erpnext/education/doctype/course/test_course.py
@@ -1,12 +1,11 @@
-# -*- coding: utf-8 -*-
# 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 3aa1ea0..c1d8242 100644
--- a/erpnext/education/doctype/course_activity/course_activity.py
+++ b/erpnext/education/doctype/course_activity/course_activity.py
@@ -1,12 +1,12 @@
-# -*- 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
+
class CourseActivity(Document):
def validate(self):
self.check_if_enrolled()
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..9514ff1 100644
--- a/erpnext/education/doctype/course_activity/test_course_activity.py
+++ b/erpnext/education/doctype/course_activity/test_course_activity.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
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..4223741 100644
--- a/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py
+++ b/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py
@@ -1,10 +1,9 @@
-# -*- 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.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..abc370e 100644
--- a/erpnext/education/doctype/course_content/course_content.py
+++ b/erpnext/education/doctype/course_content/course_content.py
@@ -1,10 +1,9 @@
-# -*- 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 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..49f042e 100644
--- a/erpnext/education/doctype/course_content/test_course_content.py
+++ b/erpnext/education/doctype/course_content/test_course_content.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..7921284 100644
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/course_enrollment.py
@@ -1,13 +1,14 @@
-# -*- 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 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 37972fe..14a7a8f 100644
--- a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py
+++ b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py
@@ -1,8 +1,9 @@
# 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',
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 874bf12..e74d510 100644
--- a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
@@ -1,16 +1,14 @@
-# -*- coding: utf-8 -*-
# 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.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):
diff --git a/erpnext/education/doctype/course_schedule/course_schedule.json b/erpnext/education/doctype/course_schedule/course_schedule.json
index 8c6746b..38d9b50 100644
--- a/erpnext/education/doctype/course_schedule/course_schedule.json
+++ b/erpnext/education/doctype/course_schedule/course_schedule.json
@@ -1,520 +1,143 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
"allow_import": 1,
- "allow_rename": 0,
"autoname": "naming_series:",
- "beta": 0,
"creation": "2015-09-09 16:34:04.960369",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
- "editable_grid": 0,
"engine": "InnoDB",
+ "field_order": [
+ "student_group",
+ "instructor",
+ "instructor_name",
+ "column_break_2",
+ "naming_series",
+ "course",
+ "color",
+ "section_break_6",
+ "schedule_date",
+ "room",
+ "column_break_9",
+ "from_time",
+ "to_time",
+ "title"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "student_group",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
"in_global_search": 1,
- "in_list_view": 0,
"in_standard_filter": 1,
"label": "Student Group",
- "length": 0,
- "no_copy": 0,
"options": "Student Group",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "instructor",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
"in_standard_filter": 1,
"label": "Instructor",
- "length": 0,
- "no_copy": 0,
"options": "Instructor",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "instructor.Instructor_name",
+ "fetch_from": "instructor.instructor_name",
"fieldname": "instructor_name",
"fieldtype": "Read Only",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
"in_global_search": 1,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Instructor Name",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
"fieldname": "naming_series",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Naming Series",
- "length": 0,
- "no_copy": 0,
"options": "EDU-CSH-.YYYY.-",
- "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": 1,
- "translatable": 0,
- "unique": 0
+ "set_only_once": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "course",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
"in_global_search": 1,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Course",
- "length": 0,
- "no_copy": 0,
"options": "Course",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "color",
"fieldtype": "Color",
- "hidden": 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": "Color",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "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
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Today",
"fieldname": "schedule_date",
"fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Schedule Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Schedule Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "room",
"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": "Room",
- "length": 0,
- "no_copy": 0,
"options": "Room",
- "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,
"fieldname": "column_break_9",
- "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,
"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
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 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
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Title",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Title"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2018-08-21 14:44:51.827225",
+ "links": [],
+ "modified": "2021-11-24 11:57:08.164449",
"modified_by": "Administrator",
"module": "Education",
"name": "Course Schedule",
- "name_case": "",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
"restrict_to_domain": "Education",
- "show_name_in_global_search": 0,
"sort_field": "schedule_date",
"sort_order": "DESC",
- "title_field": "title",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "title_field": "title"
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/course_schedule/course_schedule.py b/erpnext/education/doctype/course_schedule/course_schedule.py
index 748728d..ffd323d 100644
--- a/erpnext/education/doctype/course_schedule/course_schedule.py
+++ b/erpnext/education/doctype/course_schedule/course_schedule.py
@@ -1,12 +1,13 @@
- # -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies 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 CourseSchedule(Document):
def validate(self):
self.instructor_name = frappe.db.get_value("Instructor", self.instructor, "instructor_name")
diff --git a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
index 22ce7e1..256e40b 100644
--- a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
+++ b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
@@ -1,8 +1,9 @@
# 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',
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 5bb4de8..a732419 100644
--- a/erpnext/education/doctype/course_schedule/test_course_schedule.py
+++ b/erpnext/education/doctype/course_schedule/test_course_schedule.py
@@ -1,15 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
import datetime
-from frappe.utils import today, to_timedelta
+import unittest
+
+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')
diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.json b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.json
index 2926fe8..13dfe38 100644
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.json
+++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.json
@@ -1,661 +1,168 @@
{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2015-09-23 15:37:38.108475",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "actions": [],
+ "allow_copy": 1,
+ "creation": "2015-09-23 15:37:38.108475",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+ "student_group",
+ "course",
+ "program",
+ "column_break_3",
+ "academic_year",
+ "academic_term",
+ "section_break_6",
+ "instructor",
+ "instructor_name",
+ "column_break_9",
+ "room",
+ "section_break_7",
+ "course_start_date",
+ "course_end_date",
+ "day",
+ "reschedule",
+ "column_break_15",
+ "from_time",
+ "to_time"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "student_group",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Student Group",
- "length": 0,
- "no_copy": 0,
- "options": "Student Group",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "student_group",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Student Group",
+ "options": "Student Group",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "course",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Course",
- "length": 0,
- "no_copy": 0,
- "options": "Course",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "course",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Course",
+ "options": "Course",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "program",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Program",
- "length": 0,
- "no_copy": 0,
- "options": "Program",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "program",
+ "fieldtype": "Link",
+ "label": "Program",
+ "options": "Program",
+ "read_only": 1
+ },
{
- "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
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "academic_year",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Academic Year",
- "length": 0,
- "no_copy": 0,
- "options": "Academic Year",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "academic_year",
+ "fieldtype": "Link",
+ "label": "Academic Year",
+ "options": "Academic Year",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "academic_term",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Academic Term",
- "length": 0,
- "no_copy": 0,
- "options": "Academic Term",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "academic_term",
+ "fieldtype": "Link",
+ "label": "Academic Term",
+ "options": "Academic Term",
+ "read_only": 1
+ },
{
- "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
- },
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "instructor",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Instructor",
- "length": 0,
- "no_copy": 0,
- "options": "Instructor",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "instructor",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Instructor",
+ "options": "Instructor",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fetch_from": "instructor.instructor_name",
- "fieldname": "instructor_name",
- "fieldtype": "Read Only",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Instructor Name",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "instructor_name",
+ "fieldtype": "Read Only",
+ "label": "Instructor Name",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_9",
- "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_9",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "room",
- "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": "Room",
- "length": 0,
- "no_copy": 0,
- "options": "Room",
- "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": "room",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Room",
+ "options": "Room",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_7",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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": "section_break_7",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "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": 0,
- "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
- },
+ "fieldname": "from_time",
+ "fieldtype": "Time",
+ "label": "From Time",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "course_start_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Course Start Date",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "course_start_date",
+ "fieldtype": "Date",
+ "label": "Course Start Date",
+ "reqd": 1
+ },
{
- "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": 0,
- "in_standard_filter": 0,
- "label": "Day",
- "length": 0,
- "no_copy": 0,
- "options": "\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
- "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": "day",
+ "fieldtype": "Select",
+ "label": "Day",
+ "options": "\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reschedule",
- "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": "Reschedule",
- "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": "reschedule",
+ "fieldtype": "Check",
+ "label": "Reschedule"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_15",
- "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_15",
+ "fieldtype": "Column Break"
+ },
{
- "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": 0,
- "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
- },
+ "fieldname": "to_time",
+ "fieldtype": "Time",
+ "label": "To TIme",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "course_end_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Course End Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "course_end_date",
+ "fieldtype": "Date",
+ "label": "Course End Date",
+ "reqd": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 1,
- "hide_toolbar": 1,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2018-05-16 22:43:29.363798",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course Scheduling Tool",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "hide_toolbar": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-11-11 09:33:18.874445",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Course Scheduling Tool",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Academics User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
+ "create": 1,
+ "read": 1,
+ "role": "Academics User",
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "restrict_to_domain": "Education",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
index 0f2ea96..a309e46 100644
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
+++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
@@ -1,13 +1,14 @@
-# -*- 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
+
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 +96,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..559214b 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..3c1cabf 100644
--- a/erpnext/education/doctype/course_topic/course_topic.py
+++ b/erpnext/education/doctype/course_topic/course_topic.py
@@ -1,10 +1,9 @@
-# -*- 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 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..a4d370c 100644
--- a/erpnext/education/doctype/course_topic/test_course_topic.py
+++ b/erpnext/education/doctype/course_topic/test_course_topic.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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 6c7e95c..13123be 100644
--- a/erpnext/education/doctype/education_settings/education_settings.py
+++ b/erpnext/education/doctype/education_settings/education_settings.py
@@ -1,8 +1,7 @@
-# -*- 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
import frappe.defaults
from frappe.model.document import Document
diff --git a/erpnext/education/doctype/education_settings/test_education_settings.py b/erpnext/education/doctype/education_settings/test_education_settings.py
index 038fb6e..223e838 100644
--- a/erpnext/education/doctype/education_settings/test_education_settings.py
+++ b/erpnext/education/doctype/education_settings/test_education_settings.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..1faa0c5 100644
--- a/erpnext/education/doctype/fee_category/fee_category.py
+++ b/erpnext/education/doctype/fee_category/fee_category.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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..9e74c7d 100644
--- a/erpnext/education/doctype/fee_category/test_fee_category.py
+++ b/erpnext/education/doctype/fee_category/test_fee_category.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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..c5cf7d9 100644
--- a/erpnext/education/doctype/fee_component/fee_component.py
+++ b/erpnext/education/doctype/fee_component/fee_component.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class FeeComponent(Document):
pass
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.py b/erpnext/education/doctype/fee_schedule/fee_schedule.py
index 0b025c7..a122fe8 100644
--- a/erpnext/education/doctype/fee_schedule/fee_schedule.py
+++ b/erpnext/education/doctype/fee_schedule/fee_schedule.py
@@ -1,15 +1,15 @@
-# -*- 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, 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 4d7da21..f5d1dee 100644
--- a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py
+++ b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py
@@ -1,6 +1,6 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
def get_data():
return {
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..c291aed 100644
--- a/erpnext/education/doctype/fee_schedule/test_fee_schedule.py
+++ b/erpnext/education/doctype/fee_schedule/test_fee_schedule.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..ad7af3a 100644
--- a/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py
+++ b/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py
@@ -1,9 +1,9 @@
-# -*- 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 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..24e5404 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
@@ -1,9 +1,9 @@
-# -*- 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 FeeScheduleStudentGroup(Document):
pass
diff --git a/erpnext/education/doctype/fee_structure/fee_structure.py b/erpnext/education/doctype/fee_structure/fee_structure.py
index 9755717..9090a6b 100644
--- a/erpnext/education/doctype/fee_structure/fee_structure.py
+++ b/erpnext/education/doctype/fee_structure/fee_structure.py
@@ -1,8 +1,7 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies 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
diff --git a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py b/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
index fdf7df7..27ce06b 100644
--- a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
+++ b/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
@@ -1,8 +1,9 @@
# 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',
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..61381a6 100644
--- a/erpnext/education/doctype/fee_structure/test_fee_structure.py
+++ b/erpnext/education/doctype/fee_structure/test_fee_structure.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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 7e86704..41d428d 100644
--- a/erpnext/education/doctype/fees/fees.py
+++ b/erpnext/education/doctype/fees/fees.py
@@ -1,16 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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):
diff --git a/erpnext/education/doctype/fees/test_fees.py b/erpnext/education/doctype/fees/test_fees.py
index c6bb704..72f1d11 100644
--- a/erpnext/education/doctype/fees/test_fees.py
+++ b/erpnext/education/doctype/fees/test_fees.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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 0e73297..c4bd158 100644
--- a/erpnext/education/doctype/grading_scale/grading_scale.py
+++ b/erpnext/education/doctype/grading_scale/grading_scale.py
@@ -1,12 +1,12 @@
-# -*- 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 cint
from frappe.model.document import Document
+from frappe.utils import cint
+
class GradingScale(Document):
def validate(self):
diff --git a/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py b/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py
index 2a3f13b..b8ae8b0 100644
--- a/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py
+++ b/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py
@@ -1,6 +1,6 @@
-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.py b/erpnext/education/doctype/grading_scale/test_grading_scale.py
index 5364d7c..3ebefda 100644
--- a/erpnext/education/doctype/grading_scale/test_grading_scale.py
+++ b/erpnext/education/doctype/grading_scale/test_grading_scale.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..b4101bd 100644
--- a/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py
+++ b/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py
@@ -1,10 +1,9 @@
-# -*- 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.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..aae651b 100644
--- a/erpnext/education/doctype/guardian/guardian.py
+++ b/erpnext/education/doctype/guardian/guardian.py
@@ -1,13 +1,13 @@
-# -*- 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.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.py b/erpnext/education/doctype/guardian/test_guardian.py
index 61420f6..f474ed5 100644
--- a/erpnext/education/doctype/guardian/test_guardian.py
+++ b/erpnext/education/doctype/guardian/test_guardian.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..6cd1e55 100644
--- a/erpnext/education/doctype/guardian_interest/guardian_interest.py
+++ b/erpnext/education/doctype/guardian_interest/guardian_interest.py
@@ -1,10 +1,9 @@
-# -*- 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.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..4c29575 100644
--- a/erpnext/education/doctype/guardian_student/guardian_student.py
+++ b/erpnext/education/doctype/guardian_student/guardian_student.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class GuardianStudent(Document):
pass
diff --git a/erpnext/education/doctype/instructor/instructor.py b/erpnext/education/doctype/instructor/instructor.py
index b1bfcbb..0076240 100644
--- a/erpnext/education/doctype/instructor/instructor.py
+++ b/erpnext/education/doctype/instructor/instructor.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies 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.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 c19c859..eae67ac 100644
--- a/erpnext/education/doctype/instructor/instructor_dashboard.py
+++ b/erpnext/education/doctype/instructor/instructor_dashboard.py
@@ -1,8 +1,9 @@
# 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,
diff --git a/erpnext/education/doctype/instructor/test_instructor.py b/erpnext/education/doctype/instructor/test_instructor.py
index 4061422..4eab37a 100644
--- a/erpnext/education/doctype/instructor/test_instructor.py
+++ b/erpnext/education/doctype/instructor/test_instructor.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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..12d11ba 100644
--- a/erpnext/education/doctype/instructor_log/instructor_log.py
+++ b/erpnext/education/doctype/instructor_log/instructor_log.py
@@ -1,10 +1,9 @@
-# -*- 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.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..968a772 100644
--- a/erpnext/education/doctype/options/options.py
+++ b/erpnext/education/doctype/options/options.py
@@ -1,10 +1,9 @@
-# -*- 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 Options(Document):
pass
diff --git a/erpnext/education/doctype/program/program.py b/erpnext/education/doctype/program/program.py
index 9d886b7..a9ce644 100644
--- a/erpnext/education/doctype/program/program.py
+++ b/erpnext/education/doctype/program/program.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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):
diff --git a/erpnext/education/doctype/program/program_dashboard.py b/erpnext/education/doctype/program/program_dashboard.py
index 6c503e1..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',
diff --git a/erpnext/education/doctype/program/test_program.py b/erpnext/education/doctype/program/test_program.py
index 204f296..cb8926b 100644
--- a/erpnext/education/doctype/program/test_program.py
+++ b/erpnext/education/doctype/program/test_program.py
@@ -1,13 +1,12 @@
-# -*- coding: utf-8 -*-
# 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",
diff --git a/erpnext/education/doctype/program_course/program_course.py b/erpnext/education/doctype/program_course/program_course.py
index 684b6fa..dec392c 100644
--- a/erpnext/education/doctype/program_course/program_course.py
+++ b/erpnext/education/doctype/program_course/program_course.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# 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.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py
index dd4aa57..a23d492 100644
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.py
+++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py
@@ -1,14 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# 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):
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py b/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py
index c47f866..14ed95d 100644
--- a/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py
+++ b/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'program_enrollment',
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 497ee28..dda2465 100644
--- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
+++ b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
@@ -1,15 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and Contributors
# 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):
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..8b2d82c 100644
--- a/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py
+++ b/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py
@@ -1,10 +1,9 @@
-# -*- 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.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..17d410f 100644
--- a/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py
+++ b/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# 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..7ffa077 100644
--- a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py
+++ b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe 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 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..e806792 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..b37e5d3 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# 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..e9a0be1 100644
--- a/erpnext/education/doctype/program_fee/program_fee.py
+++ b/erpnext/education/doctype/program_fee/program_fee.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# 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 fb3b504..aa6cf9f 100644
--- a/erpnext/education/doctype/question/question.py
+++ b/erpnext/education/doctype/question/question.py
@@ -1,12 +1,12 @@
-# -*- 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
+
class Question(Document):
def validate(self):
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..7506d84 100644
--- a/erpnext/education/doctype/question/test_question.py
+++ b/erpnext/education/doctype/question/test_question.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuestion(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py
index a128e1f..9ad7252d 100644
--- a/erpnext/education/doctype/quiz/quiz.py
+++ b/erpnext/education/doctype/quiz/quiz.py
@@ -1,13 +1,12 @@
-# -*- 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
-import json
from frappe import _
from frappe.model.document import Document
+
class Quiz(Document):
def validate(self):
if self.passing_score > 100:
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..a69a0c1 100644
--- a/erpnext/education/doctype/quiz/test_quiz.py
+++ b/erpnext/education/doctype/quiz/test_quiz.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..a67f82f 100644
--- a/erpnext/education/doctype/quiz_activity/quiz_activity.py
+++ b/erpnext/education/doctype/quiz_activity/quiz_activity.py
@@ -1,10 +1,9 @@
-# -*- 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 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..1040c1a 100644
--- a/erpnext/education/doctype/quiz_activity/test_quiz_activity.py
+++ b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..91641eb 100644
--- a/erpnext/education/doctype/quiz_question/quiz_question.py
+++ b/erpnext/education/doctype/quiz_question/quiz_question.py
@@ -1,10 +1,9 @@
-# -*- 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 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..615281b 100644
--- a/erpnext/education/doctype/quiz_result/quiz_result.py
+++ b/erpnext/education/doctype/quiz_result/quiz_result.py
@@ -1,10 +1,9 @@
-# -*- 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 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..12098a7 100644
--- a/erpnext/education/doctype/quiz_result/test_quiz_result.py
+++ b/erpnext/education/doctype/quiz_result/test_quiz_result.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuizResult(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/room/room.py b/erpnext/education/doctype/room/room.py
index f26e9c4..a2a8980 100644
--- a/erpnext/education/doctype/room/room.py
+++ b/erpnext/education/doctype/room/room.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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 7bcb97f..b710722 100644
--- a/erpnext/education/doctype/room/room_dashboard.py
+++ b/erpnext/education/doctype/room/room_dashboard.py
@@ -1,8 +1,9 @@
# 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',
diff --git a/erpnext/education/doctype/room/test_room.py b/erpnext/education/doctype/room/test_room.py
index 33ffd91..68c97c7 100644
--- a/erpnext/education/doctype/room/test_room.py
+++ b/erpnext/education/doctype/room/test_room.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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..52e0508 100644
--- a/erpnext/education/doctype/school_house/school_house.py
+++ b/erpnext/education/doctype/school_house/school_house.py
@@ -1,10 +1,9 @@
-# -*- 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.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..7fe12d7 100644
--- a/erpnext/education/doctype/school_house/test_school_house.py
+++ b/erpnext/education/doctype/school_house/test_school_house.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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.py b/erpnext/education/doctype/student/student.py
index 6be9e71..44a3277 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -1,14 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies 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 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 +136,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..3ae772d 100644
--- a/erpnext/education/doctype/student/student_dashboard.py
+++ b/erpnext/education/doctype/student/student_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'heatmap': True,
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 fcb2b5f..0a85708 100644
--- a/erpnext/education/doctype/student/test_student.py
+++ b/erpnext/education/doctype/student/test_student.py
@@ -1,13 +1,11 @@
-# -*- coding: utf-8 -*-
# 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):
diff --git a/erpnext/education/doctype/student_admission/student_admission.py b/erpnext/education/doctype/student_admission/student_admission.py
index 0febb96..b1fd780 100644
--- a/erpnext/education/doctype/student_admission/student_admission.py
+++ b/erpnext/education/doctype/student_admission/student_admission.py
@@ -1,8 +1,7 @@
-# -*- 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 nowdate
diff --git a/erpnext/education/doctype/student_admission/test_student_admission.py b/erpnext/education/doctype/student_admission/test_student_admission.py
index 748c7ae..03867e2 100644
--- a/erpnext/education/doctype/student_admission/test_student_admission.py
+++ b/erpnext/education/doctype/student_admission/test_student_admission.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..eba2239 100644
--- a/erpnext/education/doctype/student_admission_program/student_admission_program.py
+++ b/erpnext/education/doctype/student_admission_program/student_admission_program.py
@@ -1,9 +1,9 @@
-# -*- 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 StudentAdmissionProgram(Document):
pass
diff --git a/erpnext/education/doctype/student_applicant/student_applicant.py b/erpnext/education/doctype/student_applicant/student_applicant.py
index 193b6d3..5dae9f6 100644
--- a/erpnext/education/doctype/student_applicant/student_applicant.py
+++ b/erpnext/education/doctype/student_applicant/student_applicant.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-777777yyy
# Copyright (c) 2015, Frappe Technologies and contributors
# 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):
diff --git a/erpnext/education/doctype/student_applicant/test_student_applicant.py b/erpnext/education/doctype/student_applicant/test_student_applicant.py
index 9734a88..ba2e9c1 100644
--- a/erpnext/education/doctype/student_applicant/test_student_applicant.py
+++ b/erpnext/education/doctype/student_applicant/test_student_applicant.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py
index 2e9e6cf..db0fd37 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.py
+++ b/erpnext/education/doctype/student_attendance/student_attendance.py
@@ -1,16 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies 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 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 e405b8a..6758452 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py
+++ b/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'reports': [
diff --git a/erpnext/education/doctype/student_attendance/test_student_attendance.py b/erpnext/education/doctype/student_attendance/test_student_attendance.py
index 9f41538..6a43e30 100644
--- a/erpnext/education/doctype/student_attendance/test_student_attendance.py
+++ b/erpnext/education/doctype/student_attendance/test_student_attendance.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# 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 972973f..7deb6b1 100644
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
+++ b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class StudentAttendanceTool(Document):
pass
@@ -14,24 +14,36 @@
student_list = []
student_attendance_list = []
- if based_on=="Course Schedule":
+ if based_on == "Course Schedule":
student_group = frappe.db.get_value("Course Schedule", course_schedule, "student_group")
if student_group:
- student_list = frappe.get_list("Student Group Student", fields=["student", "student_name", "group_roll_number"] , \
+ student_list = frappe.get_all("Student Group Student", fields=["student", "student_name", "group_roll_number"],
filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
if not student_list:
- student_list = frappe.get_list("Student Group Student", fields=["student", "student_name", "group_roll_number"] ,
+ student_list = frappe.get_all("Student Group Student", fields=["student", "student_name", "group_roll_number"],
filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
+ table = frappe.qb.DocType("Student Attendance")
+
if course_schedule:
- student_attendance_list= frappe.db.sql('''select student, status from `tabStudent Attendance` where \
- course_schedule= %s''', (course_schedule), as_dict=1)
+ student_attendance_list = (
+ frappe.qb.from_(table)
+ .select(table.student, table.status)
+ .where(
+ (table.course_schedule == course_schedule)
+ )
+ ).run(as_dict=True)
else:
- student_attendance_list= frappe.db.sql('''select student, status from `tabStudent Attendance` where \
- student_group= %s and date= %s and \
- (course_schedule is Null or course_schedule='')''',
- (student_group, date), as_dict=1)
+ student_attendance_list = (
+ frappe.qb.from_(table)
+ .select(table.student, table.status)
+ .where(
+ (table.student_group == student_group)
+ & (table.date == date)
+ & (table.course_schedule == "") | (table.course_schedule.isnull())
+ )
+ ).run(as_dict=True)
for attendance in student_attendance_list:
for student in student_list:
diff --git a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.py b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.py
index ffc42af..c15036f 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..ae59291 100644
--- a/erpnext/education/doctype/student_batch_name/student_batch_name.py
+++ b/erpnext/education/doctype/student_batch_name/student_batch_name.py
@@ -1,10 +1,9 @@
-# -*- 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.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..ad9b545 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
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..0d71859 100644
--- a/erpnext/education/doctype/student_category/student_category.py
+++ b/erpnext/education/doctype/student_category/student_category.py
@@ -1,10 +1,9 @@
-# -*- 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.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..ebb639e 100644
--- a/erpnext/education/doctype/student_category/student_category_dashboard.py
+++ b/erpnext/education/doctype/student_category/student_category_dashboard.py
@@ -1,6 +1,6 @@
-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..76469ff 100644
--- a/erpnext/education/doctype/student_category/test_student_category.py
+++ b/erpnext/education/doctype/student_category/test_student_category.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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.py b/erpnext/education/doctype/student_group/student_group.py
index 3d4572a..ceae036 100644
--- a/erpnext/education/doctype/student_group/student_group.py
+++ b/erpnext/education/doctype/student_group/student_group.py
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies 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.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()
diff --git a/erpnext/education/doctype/student_group/student_group_dashboard.py b/erpnext/education/doctype/student_group/student_group_dashboard.py
index d37445f..d5b9302 100644
--- a/erpnext/education/doctype/student_group/student_group_dashboard.py
+++ b/erpnext/education/doctype/student_group/student_group_dashboard.py
@@ -1,8 +1,9 @@
# 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',
diff --git a/erpnext/education/doctype/student_group/test_student_group.py b/erpnext/education/doctype/student_group/test_student_group.py
index 8b9b47d..807c632 100644
--- a/erpnext/education/doctype/student_group/test_student_group.py
+++ b/erpnext/education/doctype/student_group/test_student_group.py
@@ -1,12 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
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.py b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py
index 28ff7d6..8fbfcec 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
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe 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 erpnext.education.doctype.student_group.student_group import get_students
+
class StudentGroupCreationTool(Document):
@frappe.whitelist()
def get_courses(self):
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..8722f97 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..78e4541 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
@@ -1,10 +1,9 @@
-# -*- 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.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..05ef6fc 100644
--- a/erpnext/education/doctype/student_group_instructor/student_group_instructor.py
+++ b/erpnext/education/doctype/student_group_instructor/student_group_instructor.py
@@ -1,10 +1,9 @@
-# -*- 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.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 1fe4ea1..f9d00ab 100644
--- a/erpnext/education/doctype/student_group_student/student_group_student.py
+++ b/erpnext/education/doctype/student_group_student/student_group_student.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentGroupStudent(Document):
pass
diff --git a/erpnext/education/doctype/student_guardian/student_guardian.py b/erpnext/education/doctype/student_guardian/student_guardian.py
index 04445bc..0843acf 100644
--- a/erpnext/education/doctype/student_guardian/student_guardian.py
+++ b/erpnext/education/doctype/student_guardian/student_guardian.py
@@ -1,10 +1,9 @@
-# -*- 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.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..d578c9a 100644
--- a/erpnext/education/doctype/student_language/student_language.py
+++ b/erpnext/education/doctype/student_language/student_language.py
@@ -1,10 +1,9 @@
-# -*- 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.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..718896c 100644
--- a/erpnext/education/doctype/student_language/test_student_language.py
+++ b/erpnext/education/doctype/student_language/test_student_language.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..b1eda9a 100644
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.py
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application.py
@@ -1,15 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# 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 0ff6d1a..d01790d 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,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'fieldname': 'leave_application',
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 9cae257..92e82c5 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
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import getdate, add_days, add_months
+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):
diff --git a/erpnext/education/doctype/student_log/student_log.py b/erpnext/education/doctype/student_log/student_log.py
index 8b12886..b95f34e 100644
--- a/erpnext/education/doctype/student_log/student_log.py
+++ b/erpnext/education/doctype/student_log/student_log.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class StudentLog(Document):
pass
diff --git a/erpnext/education/doctype/student_log/test_student_log.py b/erpnext/education/doctype/student_log/test_student_log.py
index 1fe191f..91fdb3c 100644
--- a/erpnext/education/doctype/student_log/test_student_log.py
+++ b/erpnext/education/doctype/student_log/test_student_log.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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.py b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py
index 17bc367..43802ab 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
@@ -1,15 +1,18 @@
-# -*- 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
+
+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..e37881f 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..9ee0667 100644
--- a/erpnext/education/doctype/student_sibling/student_sibling.py
+++ b/erpnext/education/doctype/student_sibling/student_sibling.py
@@ -1,10 +1,9 @@
-# -*- 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.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..ee89f4f 100644
--- a/erpnext/education/doctype/student_siblings/student_siblings.py
+++ b/erpnext/education/doctype/student_siblings/student_siblings.py
@@ -1,10 +1,9 @@
-# -*- 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.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..d1d664b 100644
--- a/erpnext/education/doctype/topic/test_topic.py
+++ b/erpnext/education/doctype/topic/test_topic.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestTopic(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/education/doctype/topic/topic.py b/erpnext/education/doctype/topic/topic.py
index fb680d7..146f574 100644
--- a/erpnext/education/doctype/topic/topic.py
+++ b/erpnext/education/doctype/topic/topic.py
@@ -1,13 +1,14 @@
-# -*- 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
+
import json
+
+import frappe
from frappe import _
from frappe.model.document import Document
+
class Topic(Document):
def get_contents(self):
try:
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..56bb409 100644
--- a/erpnext/education/doctype/topic_content/test_topic_content.py
+++ b/erpnext/education/doctype/topic_content/test_topic_content.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..88d0eee 100644
--- a/erpnext/education/doctype/topic_content/topic_content.py
+++ b/erpnext/education/doctype/topic_content/topic_content.py
@@ -1,10 +1,9 @@
-# -*- 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 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..c274d33 100644
--- a/erpnext/education/report/absent_student_report/absent_student_report.py
+++ b/erpnext/education/report/absent_student_report/absent_student_report.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
+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..86f2451 100644
--- a/erpnext/education/report/assessment_plan_status/assessment_plan_status.py
+++ b/erpnext/education/report/assessment_plan_status/assessment_plan_status.py
@@ -1,10 +1,11 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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..38eef68 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
@@ -1,11 +1,12 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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..b042867 100644
--- a/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
+++ b/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
@@ -1,13 +1,16 @@
# 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 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 c0ec035..0599dad 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
@@ -1,10 +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):
if not filters:
filters = {}
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..7097b80 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
@@ -1,7 +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 _
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 e2576a0..52055dc 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
@@ -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
+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/student_fee_collection/student_fee_collection.json b/erpnext/education/report/student_fee_collection/student_fee_collection.json
index 8deb865..c0229a2 100644
--- a/erpnext/education/report/student_fee_collection/student_fee_collection.json
+++ b/erpnext/education/report/student_fee_collection/student_fee_collection.json
@@ -1,5 +1,5 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2016-06-22 02:58:41.024538",
"disable_prepared_report": 0,
"disabled": 0,
@@ -13,7 +13,7 @@
"name": "Student Fee Collection",
"owner": "Administrator",
"prepared_report": 0,
- "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
+ "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nWHERE\n docstatus=1 \nGROUP BY\n student",
"ref_doctype": "Fees",
"report_name": "Student Fee Collection",
"report_type": "Query Report",
@@ -22,4 +22,4 @@
"role": "Academics User"
}
]
-}
\ No newline at end of file
+}
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
index 04dc8c0..1166a75 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
@@ -1,15 +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
-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/setup.py b/erpnext/education/setup.py
index 5c40928..b716926 100644
--- a/erpnext/education/setup.py
+++ b/erpnext/education/setup.py
@@ -1,8 +1,6 @@
-# -*- 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 erpnext.setup.utils import insert_record
diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py
index 3070e6a..a7a15d1 100644
--- a/erpnext/education/utils.py
+++ b/erpnext/education/utils.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
-from __future__ import unicode_literals, division
import frappe
from frappe import _
+
class OverlapError(frappe.ValidationError): pass
def validate_overlap_for(doc, doctype, fieldname, value=None):
@@ -219,7 +218,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.py b/erpnext/education/web_form/student_applicant/student_applicant.py
index 2334f8b..02e3e93 100644
--- a/erpnext/education/web_form/student_applicant/student_applicant.py
+++ b/erpnext/education/web_form/student_applicant/student_applicant.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
diff --git a/erpnext/education/workspace/education/education.json b/erpnext/education/workspace/education/education.json
index c58ddd6..1465295 100644
--- a/erpnext/education/workspace/education/education.json
+++ b/erpnext/education/workspace/education/education.json
@@ -1,5 +1,4 @@
{
- "category": "",
"charts": [
{
"chart_name": "Program Enrollments",
@@ -8,18 +7,12 @@
],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Education\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Program Enrollments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Instructor\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Program\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fees\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course Scheduling Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Attendance Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Student and Instructor\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Content Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Admission\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fees\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Schedule\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"LMS Activity\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
"creation": "2020-03-02 17:22:57.066401",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "education",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Education",
"links": [
{
@@ -699,15 +692,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:57.929275",
+ "modified": "2021-08-05 12:15:57.929276",
"modified_by": "Administrator",
"module": "Education",
"name": "Education",
- "onboarding": "Education",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "Education",
"roles": [],
diff --git a/erpnext/erpnext_integrations/connectors/github_connection.py b/erpnext/erpnext_integrations/connectors/github_connection.py
index ab7708d..f28065e 100644
--- a/erpnext/erpnext_integrations/connectors/github_connection.py
+++ b/erpnext/erpnext_integrations/connectors/github_connection.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
import frappe
from frappe.data_migration.doctype.data_migration_connector.connectors.base import BaseConnection
from github import Github
diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
index a505ee0..9409485 100644
--- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
+++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
@@ -1,8 +1,12 @@
+import base64
+import hashlib
+import hmac
+import json
-from __future__ import unicode_literals
-import frappe, base64, hashlib, hmac, json
-from frappe.utils import cstr
+import frappe
from frappe import _
+from frappe.utils import cstr
+
def verify_request():
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
@@ -15,8 +19,7 @@
)
if frappe.request.data and \
- frappe.get_request_header("X-Wc-Webhook-Signature") and \
- not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
+ not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode():
frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(woocommerce_settings.creation_user)
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..1d0dfa5 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,6 @@
-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..212f81b 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,3 @@
-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..66826ba 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
@@ -1,12 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# 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():
@@ -24,7 +30,7 @@
#Get ASIN Codes
string_io = StringIO(frappe.safe_decode(listings_response.original))
- csv_rows = list(csv.reader(string_io, delimiter=str('\t')))
+ csv_rows = list(csv.reader(string_io, delimiter='\t'))
asin_list = list(set([row[1] for row in csv_rows[1:]]))
#break into chunks of 10
asin_chunked_list = list(chunks(asin_list, 10))
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..4caf137 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
@@ -1,29 +1,27 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# Basic interface to Amazon MWS
# Based on http://code.google.com/p/amazon-mws-python
# 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.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py
index 9c59840..c1f460f 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
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# 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..4be7960 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 99ede8f..d9dfc6f 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
Created on Tue Jun 26 15:42:07 2012
@@ -6,10 +5,9 @@
@author: pierre
"""
-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 +86,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()
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
index bff928c..e84093c 100644
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
@@ -1,12 +1,12 @@
-# -*- 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
-import requests
+
import frappe
+import requests
from frappe import _
+from frappe.model.document import Document
+
class ExotelSettings(Document):
def validate(self):
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py b/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
index 9c9df65..bceb3ca 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
@@ -1,9 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# 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..0c1952a 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and Contributors
# See license.txt
-from __future__ import unicode_literals
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..bb62c39 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
@@ -1,12 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# 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..e242ace 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
@@ -1,15 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# 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..379afe5 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestGoCardlessSettings(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
index d1adeee..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",
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 139e2fb..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 = {
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
index de93357..e7b4a30 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -1,20 +1,22 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt
-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 +41,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 +75,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))
@@ -135,6 +141,9 @@
transaction_response = frappe._dict(kwargs["Body"]["stkCallback"])
checkout_id = getattr(transaction_response, "CheckoutRequestID", "")
+ if not isinstance(checkout_id, str):
+ frappe.throw(_("Invalid Checkout Request ID"))
+
integration_request = frappe.get_doc("Integration Request", checkout_id)
transaction_data = frappe._dict(loads(integration_request.data))
total_paid = 0 # for multiple integration request made against a pos invoice
@@ -225,6 +234,9 @@
account_balance_response = frappe._dict(kwargs["Result"])
conversation_id = getattr(account_balance_response, "ConversationID", "")
+ if not isinstance(conversation_id, str):
+ frappe.throw(_("Invalid Conversation ID"))
+
request = frappe.get_doc("Integration Request", conversation_id)
if request.status == "Completed":
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 d4cb6b9..3945afa 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -1,14 +1,19 @@
-# -*- coding: utf-8 -*-
# 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
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 73f5927..0b552f9 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -1,13 +1,11 @@
-# -*- coding: utf-8 -*-
# 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():
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index eddcb34..7e6f146 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -1,19 +1,19 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
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 +84,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:
@@ -196,6 +194,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 +203,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..535d7fa 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
@@ -6,11 +5,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..5de5682 100644
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
+++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
@@ -1,17 +1,19 @@
-# -*- 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 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 +255,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..92e79ec 100644
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.py
+++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuickBooksMigrator(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
index 907a223..54ed6f7 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
@@ -1,8 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
import json
import re
@@ -11,19 +9,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 +264,7 @@
self.is_master_data_processed = 1
- except:
+ except Exception:
self.publish("Process Master Data", _("Process Failed"), -1, 5)
self.log()
@@ -302,14 +300,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 +317,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 +325,7 @@
try:
item_doc = frappe.get_doc(item)
item_doc.insert()
- except:
+ except Exception:
self.log(item_doc)
try:
@@ -346,7 +344,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 +368,7 @@
if processed_voucher:
vouchers.append(processed_voucher)
frappe.db.commit()
- except:
+ except Exception:
frappe.db.rollback()
self.log(voucher)
return vouchers
@@ -494,7 +492,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 +562,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 +581,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..7a61aba 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestTallyMigration(unittest.TestCase):
pass
diff --git a/erpnext/healthcare/doctype/diagnosis/__init__.py b/erpnext/erpnext_integrations/doctype/taxjar_nexus/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/diagnosis/__init__.py
rename to erpnext/erpnext_integrations/doctype/taxjar_nexus/__init__.py
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json
new file mode 100644
index 0000000..d4d4a51
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json
@@ -0,0 +1,51 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2021-09-11 05:09:53.773838",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "region",
+ "region_code",
+ "country",
+ "country_code"
+ ],
+ "fields": [
+ {
+ "fieldname": "region",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Region"
+ },
+ {
+ "fieldname": "region_code",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Region Code"
+ },
+ {
+ "fieldname": "country",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Country"
+ },
+ {
+ "fieldname": "country_code",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Country Code"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-14 05:33:06.444710",
+ "modified_by": "Administrator",
+ "module": "ERPNext Integrations",
+ "name": "TaxJar Nexus",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py
new file mode 100644
index 0000000..c24aa8c
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class TaxJarNexus(Document):
+ pass
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/product_tax_category_data.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/product_tax_category_data.json
new file mode 100644
index 0000000..4527bb2
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/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/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
index 62d5709..2925db8 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
@@ -5,5 +5,33 @@
is_sandbox: (frm) => {
frm.toggle_reqd("api_key", !frm.doc.is_sandbox);
frm.toggle_reqd("sandbox_api_key", frm.doc.is_sandbox);
- }
+ },
+
+ on_load: (frm) => {
+ frm.set_query('shipping_account_head', function() {
+ return {
+ filters: {
+ 'company': frm.doc.company
+ }
+ };
+ });
+ frm.set_query('tax_account_head', function() {
+ return {
+ filters: {
+ 'company': frm.doc.company
+ }
+ };
+ });
+ },
+
+ refresh: (frm) => {
+ frm.add_custom_button(__('Update Nexus List'), function() {
+ frm.call({
+ doc: frm.doc,
+ method: 'update_nexus_list'
+ });
+ });
+ },
+
+
});
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
index c0d60f7..ae1f36e 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
@@ -6,17 +6,21 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "is_sandbox",
"taxjar_calculate_tax",
+ "is_sandbox",
"taxjar_create_transactions",
"credentials",
"api_key",
"cb_keys",
"sandbox_api_key",
"configuration",
+ "company",
+ "column_break_10",
"tax_account_head",
"configuration_cb",
- "shipping_account_head"
+ "shipping_account_head",
+ "section_break_12",
+ "nexus"
],
"fields": [
{
@@ -54,6 +58,7 @@
},
{
"default": "0",
+ "depends_on": "taxjar_calculate_tax",
"fieldname": "is_sandbox",
"fieldtype": "Check",
"label": "Sandbox Mode"
@@ -64,11 +69,8 @@
"label": "Sandbox API Key"
},
{
- "fieldname": "configuration_cb",
- "fieldtype": "Column Break"
- },
- {
"default": "0",
+ "depends_on": "taxjar_calculate_tax",
"fieldname": "taxjar_create_transactions",
"fieldtype": "Check",
"label": "Create TaxJar Transaction"
@@ -82,11 +84,39 @@
{
"fieldname": "cb_keys",
"fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "nexus",
+ "fieldname": "section_break_12",
+ "fieldtype": "Section Break",
+ "label": "Nexus List"
+ },
+ {
+ "fieldname": "nexus",
+ "fieldtype": "Table",
+ "label": "Nexus",
+ "options": "TaxJar Nexus",
+ "read_only": 1
+ },
+ {
+ "fieldname": "configuration_cb",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company"
+ },
+ {
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break"
}
],
"issingle": 1,
"links": [],
- "modified": "2020-04-30 04:38:03.311089",
+ "migration_hash": "8ca1ea3309ed28547b19da8e6e27e96f",
+ "modified": "2021-11-30 11:17:24.647979",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "TaxJar Settings",
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
index 7f5f0f0..d4bbe88 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
@@ -1,10 +1,99 @@
-# -*- 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
+import os
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.model.document import Document
+from frappe.permissions import add_permission, update_permission_property
+
+from erpnext.erpnext_integrations.taxjar_integration import get_client
+
class TaxJarSettings(Document):
- pass
+
+ def on_update(self):
+ TAXJAR_CREATE_TRANSACTIONS = self.taxjar_create_transactions
+ TAXJAR_CALCULATE_TAX = self.taxjar_calculate_tax
+ TAXJAR_SANDBOX_MODE = self.is_sandbox
+
+ fields_already_exist = frappe.db.exists('Custom Field', {'dt': ('in', ['Item','Sales Invoice Item']), 'fieldname':'product_tax_category'})
+ fields_hidden = frappe.get_value('Custom Field', {'dt': ('in', ['Sales Invoice Item'])}, 'hidden')
+
+ if (TAXJAR_CREATE_TRANSACTIONS or TAXJAR_CALCULATE_TAX or TAXJAR_SANDBOX_MODE):
+ if not fields_already_exist:
+ add_product_tax_categories()
+ make_custom_fields()
+ add_permissions()
+ frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=False)
+
+ elif fields_already_exist and fields_hidden:
+ toggle_tax_category_fields(hidden='0')
+
+ elif fields_already_exist:
+ toggle_tax_category_fields(hidden='1')
+
+ def validate(self):
+ self.calculate_taxes_validation_for_create_transactions()
+
+ @frappe.whitelist()
+ def update_nexus_list(self):
+ client = get_client()
+ nexus = client.nexus_regions()
+
+ new_nexus_list = [frappe._dict(address) for address in nexus]
+
+ self.set('nexus', [])
+ self.set('nexus', new_nexus_list)
+ self.save()
+
+ def calculate_taxes_validation_for_create_transactions(self):
+ if not self.taxjar_calculate_tax and (self.taxjar_create_transactions or self.is_sandbox):
+ frappe.throw(frappe._('Before enabling <b>Create Transaction</b> or <b>Sandbox Mode</b>, you need to check the <b>Enable Tax Calculation</b> box'))
+
+
+def toggle_tax_category_fields(hidden):
+ frappe.set_value('Custom Field', {'dt':'Sales Invoice Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
+ frappe.set_value('Custom Field', {'dt':'Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
+
+
+def add_product_tax_categories():
+ with open(os.path.join(os.path.dirname(__file__), 'product_tax_category_data.json'), 'r') as f:
+ tax_categories = json.loads(f.read())
+ create_tax_categories(tax_categories['categories'])
+
+def create_tax_categories(data):
+ for d in data:
+ if not frappe.db.exists('Product Tax Category',{'product_tax_code':d.get('product_tax_code')}):
+ tax_category = frappe.new_doc('Product Tax Category')
+ tax_category.description = d.get("description")
+ tax_category.product_tax_code = d.get("product_tax_code")
+ tax_category.category_name = d.get("name")
+ tax_category.db_insert()
+
+def make_custom_fields(update=True):
+ custom_fields = {
+ 'Sales Invoice Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
+ label='Product Tax Category', fetch_from='item_code.product_tax_category'),
+ dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
+ label='Tax Collectable', read_only=1, options='currency'),
+ dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
+ label='Taxable Amount', read_only=1, options='currency')
+ ],
+ 'Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
+ label='Product Tax Category')
+ ]
+ }
+ create_custom_fields(custom_fields, update=update)
+
+def add_permissions():
+ doctype = "Product Tax Category"
+ for role in ('Accounts Manager', 'Accounts User', 'System Manager','Item Manager', 'Stock Manager'):
+ add_permission(doctype, role, 0)
+ update_permission_property(doctype, role, 0, 'write', 1)
+ update_permission_property(doctype, role, 0, 'create', 1)
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py
index 7cdfd00..d6f8eea 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py
@@ -1,10 +1,9 @@
-# -*- 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 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..9945823 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 45f2610..8da52f4 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
@@ -1,14 +1,14 @@
-# -*- 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.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):
diff --git a/erpnext/erpnext_integrations/stripe_integration.py b/erpnext/erpnext_integrations/stripe_integration.py
index 108b4c0..502cb5f 100644
--- a/erpnext/erpnext_integrations/stripe_integration.py
+++ b/erpnext/erpnext_integrations/stripe_integration.py
@@ -1,12 +1,11 @@
-# -*- 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
+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 +22,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()
+ return stripe_settings.finalize_request()
diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py
index f960998..a4e2157 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, flt
+
+from erpnext import get_default_company, get_region
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,11 @@
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 +33,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 +67,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 +102,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, doc.docstatus) for item in doc.items]
+
+ if from_shipping_state not in SUPPORTED_STATE_CODES:
+ from_shipping_state = get_state_code(from_address, 'Company')
+
+ 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,30 +124,48 @@
'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, docstatus):
+ tax_dict = dict(
+ id = item.get('idx'),
+ quantity = item.get('qty'),
+ unit_price = item.get('rate'),
+ product_tax_code = item.get('product_tax_category')
+ )
+
+ if docstatus == 1:
+ tax_dict.update({
+ 'sales_tax':item.get('tax_collectable')
+ })
+
+ return tax_dict
def set_sales_tax(doc, method):
if not TAXJAR_CALCULATE_TAX:
return
+ if get_region(doc.company) != 'United States':
+ return
+
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)
@@ -142,8 +175,10 @@
setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD])
return
- tax_data = validate_tax_request(tax_dict)
+ # check if delivering within a nexus
+ check_for_nexus(doc, tax_dict)
+ tax_data = validate_tax_request(tax_dict)
if tax_data is not None:
if not tax_data.amount_to_collect:
setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD])
@@ -163,9 +198,39 @@
"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_for_nexus(doc, tax_dict):
+ if not frappe.db.get_value('TaxJar Nexus', {'region_code': tax_dict["to_state"]}):
+ for item in doc.get("items"):
+ item.tax_collectable = flt(0)
+ item.taxable_amount = flt(0)
+
+ for tax in doc.taxes:
+ if tax.account_head == TAX_ACCOUNT_HEAD:
+ doc.taxes.remove(tax)
+ return
+
+def check_sales_tax_exemption(doc):
+ # if the party is exempt from sales tax, then set all tax account heads to zero
+ sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \
+ 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 +265,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)
else:
shipping_address = get_company_address_details(doc)
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index caafc08..d922d87 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -1,10 +1,14 @@
-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)
@@ -19,7 +23,6 @@
)
if frappe.request.data and \
- frappe.get_request_header(hmac_key) and \
not sig == bytes(frappe.get_request_header(hmac_key).encode()):
frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(settings.modified_by)
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
index 9f9204a..8e4f927 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Marketplace\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payments\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
"creation": "2020-08-20 19:30:48.138801",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "integration",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "ERPNext Integrations",
"links": [
{
@@ -119,15 +112,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:58.740246",
+ "modified": "2021-08-05 12:15:58.740247",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "ERPNext Integrations",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
index fd4afb8..5efafd6 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Integrations Settings\", \"col\": 4}}]",
"creation": "2020-07-31 10:38:54.021237",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "setting",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "ERPNext Integrations Settings",
"links": [
{
@@ -40,17 +33,6 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
- "label": "Shopify Settings",
- "link_count": 0,
- "link_to": "Shopify Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
"label": "Amazon MWS Settings",
"link_count": 0,
"link_to": "Amazon MWS Settings",
@@ -81,19 +63,16 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:58.951704",
+ "modified": "2021-11-23 04:30:33.106991",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "ERPNext Integrations Settings",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 11,
"shortcuts": [],
"title": "ERPNext Integrations Settings"
-}
+}
\ No newline at end of file
diff --git a/erpnext/exceptions.py b/erpnext/exceptions.py
index 04291cd..8d6f13a 100644
--- a/erpnext/exceptions.py
+++ b/erpnext/exceptions.py
@@ -1,6 +1,6 @@
-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 e494489..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")
- }
- ]
-};
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 eca7143..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'
- }
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 99b7cb2..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);
- }
- }
- });
- }
- }
-});
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 ccae129..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"
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-08-17 06:05:02.240812",
- "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/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 81a3982..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
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 ae6b39b..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js
+++ /dev/null
@@ -1,190 +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 58194f1..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py
+++ /dev/null
@@ -1,120 +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/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/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 ae44a2b..0000000
--- a/erpnext/healthcare/doctype/exercise_type/exercise_type.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.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 5b9c179..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
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 82e7136..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()
-
- # For first appointment, invoice is generated
- appointment = create_appointment(patient, practitioner, nowdate())
- invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
- self.assertEqual(invoiced, 1)
-
- # 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)
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 44c3998..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js
+++ /dev/null
@@ -1,144 +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 cb455eb..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
+++ /dev/null
@@ -1,336 +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-08-24 10:42:08.513054",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare Practitioner",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Laboratory User",
- "select": 1,
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "select": 1,
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "select": 1,
- "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 3ee3377..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
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 ff9e212..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()
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 7989762..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py
+++ /dev/null
@@ -1,142 +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/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 c3847ea..0000000
--- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py
+++ /dev/null
@@ -1,207 +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.currency = 'INR'
- 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/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/__init__.py b/erpnext/healthcare/doctype/patient/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient/__init__.py
+++ /dev/null
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 c6e489e..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ /dev/null
@@ -1,607 +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 (data.message) {
- frm.toggle_display('mode_of_payment', 1);
- frm.toggle_display('paid_amount', 1);
- frm.toggle_display('billing_item', 1);
- frm.toggle_reqd('mode_of_payment', 1);
- frm.toggle_reqd('paid_amount', 1);
- frm.toggle_reqd('billing_item', 1);
- } 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 73ec3bc..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
- },
- {
- "depends_on": "eval:doc.practitioner;",
- "fieldname": "section_break_12",
- "fieldtype": "Section Break",
- "label": "Appointment Details"
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "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-06-16 00:40:26.841794",
- "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
-}
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 ac09537..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ /dev/null
@@ -1,506 +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
-from erpnext import get_company_currency
-
-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}
- 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
- 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.currency = frappe.get_value('Customer', sales_invoice.customer, 'default_currency') \
- or get_company_currency(appointment_doc.company)
- 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)
- return not has_previous_appointment
-
-
-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 ac80612..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ /dev/null
@@ -1,359 +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, check_payment_fields_reqd, check_is_new_patient
-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 test_payment_should_be_mandatory_for_new_patient_appointment(self):
- frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
- frappe.db.set_value('Healthcare Settings', None, 'max_visits', 3)
- frappe.db.set_value('Healthcare Settings', None, 'valid_days', 30)
-
- patient = create_patient()
- assert check_is_new_patient(patient)
- payment_required = check_payment_fields_reqd(patient)
- assert payment_required is True
-
- def test_sales_invoice_should_be_generated_for_new_patient_appointment(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
- invoice_count = frappe.db.count('Sales Invoice')
-
- assert check_is_new_patient(patient)
- create_appointment(patient, practitioner, nowdate())
- new_invoice_count = frappe.db.count('Sales Invoice')
-
- assert new_invoice_count == invoice_count + 1
-
- def test_patient_appointment_should_consider_permissions_while_fetching_appointments(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- create_appointment(patient, practitioner, nowdate())
-
- patient, medical_department, new_practitioner = create_healthcare_docs(practitioner_name='Dr. John')
- create_appointment(patient, new_practitioner, nowdate())
-
- roles = [{"doctype": "Has Role", "role": "Physician"}]
- user = create_user(roles=roles)
- new_practitioner = frappe.get_doc('Healthcare Practitioner', new_practitioner)
- new_practitioner.user_id = user.email
- new_practitioner.save()
-
- frappe.set_user(user.name)
- appointments = frappe.get_list('Patient Appointment')
- assert len(appointments) == 1
-
- frappe.set_user("Administrator")
- appointments = frappe.get_list('Patient Appointment')
- assert len(appointments) == 2
-
-def create_healthcare_docs(practitioner_name=None):
- if not practitioner_name:
- practitioner_name = '_Test Healthcare Practitioner'
-
- patient = create_patient()
- practitioner = frappe.db.exists('Healthcare Practitioner', practitioner_name)
- 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 = practitioner_name
- 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.default_currency = 'INR'
- 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()
-
-def create_user(email=None, roles=None):
- if not email:
- email = '{}@frappe.com'.format(frappe.utils.random_string(10))
- user = frappe.db.exists('User', email)
- if not user:
- user = frappe.get_doc({
- "doctype": "User",
- "email": email,
- "first_name": "test_user",
- "password": "password",
- "roles": roles,
- }).insert()
- return user
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 7bad20d..0000000
--- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py
+++ /dev/null
@@ -1,33 +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 2b3029e..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)
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 63b0085..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
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 f8ccc8a..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
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 c29f6a0..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
+++ /dev/null
@@ -1,104 +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
-from erpnext import get_company_currency
-
-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')
- si.currency = frappe.get_value('Customer', si.customer, 'default_currency') \
- or get_company_currency(si.company)
-
- 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/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 635d4be..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)
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 fbfa774..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');
- }
- });
- }
- }
-});
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 a5dad29..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
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 4bb3940..0000000
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py
+++ /dev/null
@@ -1,17 +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/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 f170655..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>
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 30064bd..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>
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 4b7599d..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();
- }
-}
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 46bfb3d..0000000
--- a/erpnext/healthcare/page/patient_progress/patient_progress.py
+++ /dev/null
@@ -1,196 +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 4ee6573..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>
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 3c90adb..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": "Jinja",
- "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 4819e6d..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": "Jinja",
- "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 28b60bd..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
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/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 9a4840a..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"
- }
diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py
deleted file mode 100644
index 891272d..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"}
- ])
- }
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 f09e540..0000000
--- a/erpnext/healthcare/web_form/patient_registration/patient_registration.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.ready(function() {
- // bind events here
-});
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 4854bfd..63530ea 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
from frappe import _
app_name = "erpnext"
@@ -61,6 +60,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 +68,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 +79,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 +162,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 +174,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 +208,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 +216,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",
@@ -264,11 +251,13 @@
"on_submit": [
"erpnext.regional.create_transaction_log",
"erpnext.regional.italy.utils.sales_invoice_on_submit",
+ "erpnext.regional.saudi_arabia.utils.create_qr_code",
"erpnext.erpnext_integrations.taxjar_integration.create_transaction"
],
"on_cancel": [
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
- "erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
+ "erpnext.erpnext_integrations.taxjar_integration.delete_transaction",
+ "erpnext.regional.saudi_arabia.utils.delete_qr_code_file"
],
"on_trash": "erpnext.regional.check_deletion_permission",
"validate": [
@@ -276,6 +265,9 @@
"erpnext.regional.india.utils.update_taxable_values"
]
},
+ "POS Invoice": {
+ "on_submit": ["erpnext.regional.saudi_arabia.utils.create_qr_code"]
+ },
"Purchase Invoice": {
"validate": [
"erpnext.regional.india.utils.validate_reverse_charge_transaction",
@@ -286,11 +278,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,7 +298,7 @@
"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"
@@ -310,7 +307,11 @@
'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"]
},
"Company": {
- "on_trash": "erpnext.regional.india.utils.delete_gst_settings_for_company"
+ "on_trash": ["erpnext.regional.india.utils.delete_gst_settings_for_company",
+ "erpnext.regional.saudi_arabia.utils.delete_vat_settings_for_company"]
+ },
+ "Integration Request": {
+ "validate": "erpnext.accounts.doctype.payment_request.payment_request.validate_payment"
}
}
@@ -319,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"]
@@ -332,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": [
@@ -373,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",
@@ -431,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 = {
@@ -526,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 6a2fc02..e4bd1c8 100644
--- a/erpnext/hotels/doctype/hotel_room/hotel_room.py
+++ b/erpnext/hotels/doctype/hotel_room/hotel_room.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class HotelRoom(Document):
def validate(self):
if not self.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..95efe2c 100644
--- a/erpnext/hotels/doctype/hotel_room/test_hotel_room.py
+++ b/erpnext/hotels/doctype/hotel_room/test_hotel_room.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..1664931 100644
--- a/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py
+++ b/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py
@@ -1,10 +1,9 @@
-# -*- 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.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..aedc83a 100644
--- a/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py
+++ b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py
@@ -1,11 +1,11 @@
-# -*- 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.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..749731f 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..d28e573 100644
--- a/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py
+++ b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py
@@ -1,10 +1,9 @@
-# -*- 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.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..3455009 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..2e6bb5f 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
@@ -1,10 +1,9 @@
-# -*- 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.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..ebbdb6e 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
@@ -1,10 +1,9 @@
-# -*- 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.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..196e650 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..7725955 100644
--- a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py
+++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py
@@ -1,12 +1,14 @@
-# -*- 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, 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/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..bb32a27 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
@@ -1,11 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import HotelRoomPricingNotSetError, HotelRoomUnavailableError
+
+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..41d86dd 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
@@ -1,10 +1,9 @@
-# -*- 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.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..7ab529f 100644
--- a/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py
+++ b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py
@@ -1,10 +1,9 @@
-# -*- 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.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..8d1147d 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..8376d50 100644
--- a/erpnext/hotels/doctype/hotel_settings/hotel_settings.py
+++ b/erpnext/hotels/doctype/hotel_settings/hotel_settings.py
@@ -1,10 +1,9 @@
-# -*- 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.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..e76c00c 100644
--- a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py
+++ b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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 259edb9..c43589d 100644
--- a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py
+++ b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py
@@ -1,13 +1,14 @@
# 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 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)
diff --git a/erpnext/hr/doctype/__init__.py b/erpnext/hr/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/__init__.py
+++ b/erpnext/hr/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.py b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
index 85b82c5..0120188 100644
--- a/erpnext/hr/doctype/appointment_letter/appointment_letter.py
+++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..e0f65b4 100644
--- a/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py
+++ b/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..d158013 100644
--- a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py
+++ b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..9ac726e 100644
--- a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py
+++ b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..aa87da3 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestAppointmentLetterTemplate(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/appraisal/__init__.py b/erpnext/hr/doctype/appraisal/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/appraisal/__init__.py
+++ b/erpnext/hr/doctype/appraisal/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/appraisal/appraisal.py b/erpnext/hr/doctype/appraisal/appraisal.py
index c2ed457..83273f8 100644
--- a/erpnext/hr/doctype/appraisal/appraisal.py
+++ b/erpnext/hr/doctype/appraisal/appraisal.py
@@ -1,16 +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
+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.py b/erpnext/hr/doctype/appraisal/test_appraisal.py
index f70dc48..90c30ef 100644
--- a/erpnext/hr/doctype/appraisal/test_appraisal.py
+++ b/erpnext/hr/doctype/appraisal/test_appraisal.py
@@ -1,8 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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/__init__.py b/erpnext/hr/doctype/appraisal_goal/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/appraisal_goal/__init__.py
+++ b/erpnext/hr/doctype/appraisal_goal/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py
index 11d9f39..3cbc918 100644
--- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py
+++ b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class AppraisalGoal(Document):
pass
diff --git a/erpnext/hr/doctype/appraisal_template/__init__.py b/erpnext/hr/doctype/appraisal_template/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/appraisal_template/__init__.py
+++ b/erpnext/hr/doctype/appraisal_template/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py
index d0dfad4..6b5921e 100644
--- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py
+++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.py
@@ -1,12 +1,12 @@
# 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 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 392b370..116a3f9 100644
--- a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
+++ b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'kra_template',
diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
index e3029d9..d0e81a7 100644
--- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
+++ b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
@@ -1,8 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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/__init__.py b/erpnext/hr/doctype/appraisal_template_goal/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/appraisal_template_goal/__init__.py
+++ b/erpnext/hr/doctype/appraisal_template_goal/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 b3c5704..e6c5f64 100644
--- a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py
+++ b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class AppraisalTemplateGoal(Document):
pass
diff --git a/erpnext/hr/doctype/attendance/__init__.py b/erpnext/hr/doctype/attendance/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/attendance/__init__.py
+++ b/erpnext/hr/doctype/attendance/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index c1a7c8f..7dcfac2 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -1,15 +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
-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 +134,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_dashboard.py b/erpnext/hr/doctype/attendance/attendance_dashboard.py
index 5dd9403..4bb36a0 100644
--- a/erpnext/hr/doctype/attendance/attendance_dashboard.py
+++ b/erpnext/hr/doctype/attendance/attendance_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'attendance',
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.py b/erpnext/hr/doctype/attendance/test_attendance.py
index 838b704..a770d70 100644
--- a/erpnext/hr/doctype/attendance/test_attendance.py
+++ b/erpnext/hr/doctype/attendance/test_attendance.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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..8fbe7c7 100644
--- a/erpnext/hr/doctype/attendance_request/attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/attendance_request.py
@@ -1,14 +1,15 @@
-# -*- 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.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 2d3eb00..9197057 100644
--- a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
+++ b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'fieldname': 'attendance_request',
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..3f0442c 100644
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
@@ -1,12 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
+from datetime import date
import frappe
-import unittest
from frappe.utils import nowdate
-from datetime import date
test_dependencies = ["Employee"]
diff --git a/erpnext/hr/doctype/branch/__init__.py b/erpnext/hr/doctype/branch/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/branch/__init__.py
+++ b/erpnext/hr/doctype/branch/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/branch/branch.py b/erpnext/hr/doctype/branch/branch.py
index a847c8e..133ada0 100644
--- a/erpnext/hr/doctype/branch/branch.py
+++ b/erpnext/hr/doctype/branch/branch.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class Branch(Document):
pass
diff --git a/erpnext/hr/doctype/branch/test_branch.js b/erpnext/hr/doctype/branch/test_branch.js
deleted file mode 100644
index 82a6ae1..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()
- ]);
-});
diff --git a/erpnext/hr/doctype/branch/test_branch.py b/erpnext/hr/doctype/branch/test_branch.py
index 807698b..e84c6e4 100644
--- a/erpnext/hr/doctype/branch/test_branch.py
+++ b/erpnext/hr/doctype/branch/test_branch.py
@@ -1,7 +1,6 @@
# 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
+
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 3db8165..7d60515 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
@@ -1,14 +1,21 @@
-# -*- 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.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, \
- create_additional_leave_ledger_entry, get_holiday_dates_for_employee
+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):
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..5e51879 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
@@ -1,14 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import today, add_months, add_days
+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..fe11c47 100644
--- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py
+++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py
@@ -1,15 +1,12 @@
-# -*- 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.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
class DailyWorkSummary(Document):
@@ -82,7 +79,7 @@
crop=True
)
d.image = thumbnail_image
- except:
+ except Exception:
d.image = original_image
if d.sender in did_not_reply:
@@ -109,7 +106,7 @@
:param group: Daily Work Summary Group `name`'''
group_doc = group
- if isinstance(group_doc, string_types):
+ if isinstance(group_doc, str):
group_doc = frappe.get_doc('Daily Work Summary Group', group)
emails = get_users_email(group_doc)
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 1533517..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()
- ]);
-
-});
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..5edfb31 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
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-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 +63,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
@@ -74,7 +72,6 @@
from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
where q.name = r.parent""", as_dict=1)
- frappe.db.commit()
def setup_groups(self, hour=None):
# setup email to trigger at this hour
diff --git a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py
index ece331a..ed98168 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
@@ -1,15 +1,16 @@
-# # -*- 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.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..6e0809a 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
@@ -1,10 +1,9 @@
-# -*- 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 DailyWorkSummaryGroupUser(Document):
pass
diff --git a/erpnext/hr/doctype/department/__init__.py b/erpnext/hr/doctype/department/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/department/__init__.py
+++ b/erpnext/hr/doctype/department/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/department/department.py b/erpnext/hr/doctype/department/department.py
index 539a360..ed0bfcf 100644
--- a/erpnext/hr/doctype/department/department.py
+++ b/erpnext/hr/doctype/department/department.py
@@ -1,11 +1,12 @@
# 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.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/test_department.js b/erpnext/hr/doctype/department/test_department.js
deleted file mode 100644
index e73779c..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()
- ]);
-});
diff --git a/erpnext/hr/doctype/department/test_department.py b/erpnext/hr/doctype/department/test_department.py
index e4f6645..95bf663 100644
--- a/erpnext/hr/doctype/department/test_department.py
+++ b/erpnext/hr/doctype/department/test_department.py
@@ -1,9 +1,10 @@
# 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):
diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py
index d337959..375ae7c 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -1,12 +1,12 @@
-# -*- 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
+
class DepartmentApprover(Document):
pass
diff --git a/erpnext/hr/doctype/designation/__init__.py b/erpnext/hr/doctype/designation/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/designation/__init__.py
+++ b/erpnext/hr/doctype/designation/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/designation/designation.py b/erpnext/hr/doctype/designation/designation.py
index a3f84aa..d7be55c 100644
--- a/erpnext/hr/doctype/designation/designation.py
+++ b/erpnext/hr/doctype/designation/designation.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class Designation(Document):
pass
diff --git a/erpnext/hr/doctype/designation/test_designation.js b/erpnext/hr/doctype/designation/test_designation.js
deleted file mode 100644
index 00adf82..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()
- ]);
-});
diff --git a/erpnext/hr/doctype/designation/test_designation.py b/erpnext/hr/doctype/designation/test_designation.py
index 2778862..f2d6d36 100644
--- a/erpnext/hr/doctype/designation/test_designation.py
+++ b/erpnext/hr/doctype/designation/test_designation.py
@@ -1,9 +1,8 @@
# 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
+
# test_records = frappe.get_test_records('Designation')
def create_designation(**args):
diff --git a/erpnext/hr/doctype/designation_skill/designation_skill.py b/erpnext/hr/doctype/designation_skill/designation_skill.py
index c37d21f..c35223b 100644
--- a/erpnext/hr/doctype/designation_skill/designation_skill.py
+++ b/erpnext/hr/doctype/designation_skill/designation_skill.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 DesignationSkill(Document):
pass
diff --git a/erpnext/hr/doctype/driver/driver.py b/erpnext/hr/doctype/driver/driver.py
index 2cd22cd..2698189 100644
--- a/erpnext/hr/doctype/driver/driver.py
+++ b/erpnext/hr/doctype/driver/driver.py
@@ -1,9 +1,9 @@
-# -*- 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 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..2270729 100644
--- a/erpnext/hr/doctype/driver/test_driver.py
+++ b/erpnext/hr/doctype/driver/test_driver.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..a1a6d55 100644
--- a/erpnext/hr/doctype/driving_license_category/driving_license_category.py
+++ b/erpnext/hr/doctype/driving_license_category/driving_license_category.py
@@ -1,9 +1,9 @@
-# -*- 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 DrivingLicenseCategory(Document):
pass
diff --git a/erpnext/hr/doctype/employee/__init__.py b/erpnext/hr/doctype/employee/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/employee/__init__.py
+++ b/erpnext/hr/doctype/employee/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 643f3da..88e5ca9 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -1,15 +1,21 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
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 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):
@@ -90,15 +96,8 @@
'user': self.user_id
})
- if employee_user_permission_exists: return
-
- employee_user_permission_exists = frappe.db.exists('User Permission', {
- 'allow': 'Employee',
- 'for_value': self.name,
- 'user': self.user_id
- })
-
- if employee_user_permission_exists: return
+ if employee_user_permission_exists:
+ return
add_user_permission("Employee", self.name, self.user_id)
set_user_permission_if_allowed("Company", self.company, self.user_id)
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index e853bee..a4c0af0 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -1,6 +1,6 @@
-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
index 2155c02..559bd39 100644
--- a/erpnext/hr/doctype/employee/employee_reminders.py
+++ b/erpnext/hr/doctype/employee/employee_reminders.py
@@ -3,15 +3,17 @@
import frappe
from frappe import _
-from frappe.utils import comma_sep, getdate, today, add_months, add_days
+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") or 1)
+ 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
@@ -19,7 +21,7 @@
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") or 1)
+ 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
@@ -77,7 +79,7 @@
# ------------------
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") or 1)
+ to_send = int(frappe.db.get_single_value("HR Settings", "send_birthday_reminders"))
if not to_send:
return
@@ -155,6 +157,8 @@
AND
MONTH({condition_column}) = MONTH(%(today)s)
AND
+ YEAR({condition_column}) < YEAR(%(today)s)
+ AND
`status` = 'Active'
""",
"postgres": f"""
@@ -165,6 +169,8 @@
AND
DATE_PART('month', {condition_column}) = date_part('month', %(today)s)
AND
+ DATE_PART('year', {condition_column}) < date_part('year', %(today)s)
+ AND
"status" = 'Active'
""",
}, dict(today=today(), condition_column=condition_column), as_dict=1)
@@ -182,7 +188,7 @@
# --------------------------
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") or 1)
+ to_send = int(frappe.db.get_single_value("HR Settings", "send_work_anniversary_reminders"))
if not to_send:
return
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index 5feb6de..8a2da08 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -1,10 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-import frappe
-import erpnext
import unittest
+
+import frappe
import frappe.utils
+
+import erpnext
from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError
test_records = frappe.get_test_records('Employee')
@@ -23,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)
@@ -53,6 +55,7 @@
"email": user,
"first_name": user,
"new_password": "password",
+ "send_welcome_email": 0,
"roles": [{"doctype": "Has Role", "role": "Employee"}]
}).insert()
diff --git a/erpnext/hr/doctype/employee/test_employee_reminders.py b/erpnext/hr/doctype/employee/test_employee_reminders.py
index 7e560f5..52c0098 100644
--- a/erpnext/hr/doctype/employee/test_employee_reminders.py
+++ b/erpnext/hr/doctype/employee/test_employee_reminders.py
@@ -1,11 +1,12 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-import frappe
import unittest
-
-from frappe.utils import getdate
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
@@ -18,7 +19,7 @@
# Create a test holiday list
test_holiday_dates = cls.get_test_holiday_dates()
test_holiday_list = make_holiday_list(
- 'TestHolidayRemindersList',
+ 'TestHolidayRemindersList',
holiday_dates=[
{'holiday_date': test_holiday_dates[0], 'description': 'test holiday1'},
{'holiday_date': test_holiday_dates[1], 'description': 'test holiday2'},
@@ -49,8 +50,8 @@
def get_test_holiday_dates(cls):
today_date = getdate()
return [
- today_date,
- today_date-timedelta(days=4),
+ today_date,
+ today_date-timedelta(days=4),
today_date-timedelta(days=3),
today_date+timedelta(days=1),
today_date+timedelta(days=3),
@@ -63,7 +64,7 @@
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)))
@@ -84,7 +85,10 @@
employee.company = "_Test Company"
employee.save()
- from erpnext.hr.doctype.employee.employee_reminders import get_employees_who_are_born_today, send_birthday_reminders
+ 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"))
@@ -105,7 +109,10 @@
employee.company = "_Test Company"
employee.save()
- from erpnext.hr.doctype.employee.employee_reminders import get_employees_having_an_event_today, send_work_anniversary_reminders
+ 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"))
@@ -118,10 +125,10 @@
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.utils import get_holidays_for_employee
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")
@@ -133,10 +140,10 @@
holidays = get_holidays_for_employee(
self.test_employee.get('name'),
getdate(), getdate() + timedelta(days=3),
- only_non_weekly=True,
+ only_non_weekly=True,
raise_exception=False
)
-
+
send_holidays_reminder_in_advance(
self.test_employee.get('name'),
holidays
@@ -147,6 +154,7 @@
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
@@ -158,9 +166,10 @@
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
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..7aac2b6 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.py
@@ -1,15 +1,18 @@
-# -*- 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, erpnext
+
+import frappe
from frappe import _
from frappe.model.document import Document
+from frappe.query_builder.functions import Sum
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
@@ -39,24 +42,34 @@
self.status = "Cancelled"
def set_total_advance_paid(self):
- paid_amount = frappe.db.sql("""
- select ifnull(sum(debit), 0) as paid_amount
- from `tabGL Entry`
- where against_voucher_type = 'Employee Advance'
- and against_voucher = %s
- and party_type = 'Employee'
- and party = %s
- """, (self.name, self.employee), as_dict=1)[0].paid_amount
+ gle = frappe.qb.DocType("GL Entry")
- return_amount = frappe.db.sql("""
- select ifnull(sum(credit), 0) as return_amount
- from `tabGL Entry`
- where against_voucher_type = 'Employee Advance'
- and voucher_type != 'Expense Claim'
- and against_voucher = %s
- and party_type = 'Employee'
- and party = %s
- """, (self.name, self.employee), as_dict=1)[0].return_amount
+ paid_amount = (
+ frappe.qb.from_(gle)
+ .select(Sum(gle.debit).as_("paid_amount"))
+ .where(
+ (gle.against_voucher_type == 'Employee Advance')
+ & (gle.against_voucher == self.name)
+ & (gle.party_type == 'Employee')
+ & (gle.party == self.employee)
+ & (gle.docstatus == 1)
+ & (gle.is_cancelled == 0)
+ )
+ ).run(as_dict=True)[0].paid_amount or 0
+
+ return_amount = (
+ frappe.qb.from_(gle)
+ .select(Sum(gle.credit).as_("return_amount"))
+ .where(
+ (gle.against_voucher_type == 'Employee Advance')
+ & (gle.voucher_type != 'Expense Claim')
+ & (gle.against_voucher == self.name)
+ & (gle.party_type == 'Employee')
+ & (gle.party == self.employee)
+ & (gle.docstatus == 1)
+ & (gle.is_cancelled == 0)
+ )
+ ).run(as_dict=True)[0].return_amount or 0
if paid_amount != 0:
paid_amount = flt(paid_amount) / flt(self.exchange_rate)
@@ -168,7 +181,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..9450258 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'employee_advance',
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 100968b..5f2e720 100644
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
@@ -1,14 +1,21 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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 +34,64 @@
journal_entry1 = make_payment_entry(advance)
self.assertRaises(EmployeeAdvanceOverPayment, journal_entry1.submit)
+ def test_paid_amount_on_pe_cancellation(self):
+ employee_name = make_employee("_T@employe.advance")
+ advance = make_employee_advance(employee_name)
+
+ pe = make_payment_entry(advance)
+ pe.submit()
+
+ advance.reload()
+
+ self.assertEqual(advance.paid_amount, 1000)
+ self.assertEqual(advance.status, "Paid")
+
+ pe.cancel()
+ advance.reload()
+
+ self.assertEqual(advance.paid_amount, 0)
+ self.assertEqual(advance.status, "Unpaid")
+
+ 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 +100,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,6 +110,10 @@
doc.advance_amount = 1000
doc.posting_date = nowdate()
doc.advance_account = "_Test Employee Advance - _TC"
+
+ if args:
+ doc.update(args)
+
doc.insert()
doc.submit()
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..af2ca50 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
+++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
@@ -1,10 +1,10 @@
-# -*- 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
+
import json
+
+import frappe
from frappe.model.document import Document
from frappe.utils import getdate
@@ -53,8 +53,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 +65,4 @@
company=company
))
attendance.insert()
- attendance.submit()
+ attendance.submit()
\ 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..e824081 100644
--- a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py
+++ b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py
@@ -1,10 +1,9 @@
-# -*- 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 EmployeeBoardingActivity(Document):
pass
diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
index 6c0cd4f..c1d4ac7 100644
--- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
@@ -1,16 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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)
diff --git a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
index 7ba511f..254bf9e 100644
--- a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
@@ -1,15 +1,19 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# 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):
diff --git a/erpnext/hr/doctype/employee_education/__init__.py b/erpnext/hr/doctype/employee_education/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/employee_education/__init__.py
+++ b/erpnext/hr/doctype/employee_education/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/employee_education/employee_education.py b/erpnext/hr/doctype/employee_education/employee_education.py
index f0a7617..ded583b 100644
--- a/erpnext/hr/doctype/employee_education/employee_education.py
+++ b/erpnext/hr/doctype/employee_education/employee_education.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class EmployeeEducation(Document):
pass
diff --git a/erpnext/hr/doctype/employee_external_work_history/__init__.py b/erpnext/hr/doctype/employee_external_work_history/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/employee_external_work_history/__init__.py
+++ b/erpnext/hr/doctype/employee_external_work_history/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 517ef57..d594fbf 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class EmployeeExternalWorkHistory(Document):
pass
diff --git a/erpnext/hr/doctype/employee_grade/employee_grade.py b/erpnext/hr/doctype/employee_grade/employee_grade.py
index 42a9f16..41b7915 100644
--- a/erpnext/hr/doctype/employee_grade/employee_grade.py
+++ b/erpnext/hr/doctype/employee_grade/employee_grade.py
@@ -1,10 +1,9 @@
-# -*- 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 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 df67910..6825dae 100644
--- a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
+++ b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'transactions': [
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..a70d685 100644
--- a/erpnext/hr/doctype/employee_grade/test_employee_grade.py
+++ b/erpnext/hr/doctype/employee_grade/test_employee_grade.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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 1705582..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"]:
diff --git a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
index ed897ee..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()
diff --git a/erpnext/hr/doctype/employee_group/employee_group.py b/erpnext/hr/doctype/employee_group/employee_group.py
index 3025877..c4ce083 100644
--- a/erpnext/hr/doctype/employee_group/employee_group.py
+++ b/erpnext/hr/doctype/employee_group/employee_group.py
@@ -1,9 +1,9 @@
-# -*- 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 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 26a61c4..a87f400 100644
--- a/erpnext/hr/doctype/employee_group/test_employee_group.py
+++ b/erpnext/hr/doctype/employee_group/test_employee_group.py
@@ -1,11 +1,13 @@
-# -*- coding: utf-8 -*-
# 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
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..adf6ca2 100644
--- a/erpnext/hr/doctype/employee_group_table/employee_group_table.py
+++ b/erpnext/hr/doctype/employee_group_table/employee_group_table.py
@@ -1,9 +1,9 @@
-# -*- 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 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..4a8c437 100644
--- a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py
+++ b/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py
@@ -1,10 +1,9 @@
-# -*- 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 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..4f042b7 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeHealthInsurance(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/employee_internal_work_history/__init__.py b/erpnext/hr/doctype/employee_internal_work_history/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/employee_internal_work_history/__init__.py
+++ b/erpnext/hr/doctype/employee_internal_work_history/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 2f385a8..6225de6 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class EmployeeInternalWorkHistory(Document):
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 ca9b298..eba2a03 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
@@ -1,13 +1,14 @@
-# -*- 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 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):
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 0445270..cb1b560 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -1,14 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+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 +50,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 +69,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 +87,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',
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..199013a 100644
--- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py
+++ b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py
@@ -1,10 +1,9 @@
-# -*- 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 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 ab0eb2f..3b846a0 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,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'employee_onboarding_template',
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..db09011 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..cf6156e 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -1,13 +1,14 @@
-# -*- 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.utils import getdate
-from erpnext.hr.utils import update_employee, validate_active_employee
+
+from erpnext.hr.utils import update_employee_work_history, validate_active_employee
+
class EmployeePromotion(Document):
def validate(self):
@@ -20,10 +21,10 @@
def on_submit(self):
employee = frappe.get_doc("Employee", self.employee)
- employee = update_employee(employee, self.promotion_details, date=self.promotion_date)
+ employee = update_employee_work_history(employee, self.promotion_details, date=self.promotion_date)
employee.save()
def on_cancel(self):
employee = frappe.get_doc("Employee", self.employee)
- employee = update_employee(employee, self.promotion_details, cancel=True)
+ employee = update_employee_work_history(employee, self.promotion_details, cancel=True)
employee.save()
diff --git a/erpnext/hr/doctype/employee_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..fc9d195 100644
--- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import getdate, add_days
+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..345899e 100644
--- a/erpnext/hr/doctype/employee_property_history/employee_property_history.py
+++ b/erpnext/hr/doctype/employee_property_history/employee_property_history.py
@@ -1,10 +1,9 @@
-# -*- 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 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 547a95e..eaa42c7 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.py
@@ -1,14 +1,15 @@
-# -*- 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.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 +36,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,9 +59,9 @@
@frappe.whitelist()
def create_additional_salary(doc):
import json
- from six import string_types
- if isinstance(doc, string_types):
+
+ if isinstance(doc, str):
doc = frappe._dict(json.loads(doc))
if not frappe.db.exists("Additional Salary", {"ref_docname": doc.name}):
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
index caca296..07c2402 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'fieldname': 'employee_referral',
diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
index 599f326..529e355 100644
--- a/erpnext/hr/doctype/employee_referral/test_employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
@@ -1,16 +1,25 @@
-# -*- 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 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
+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 +53,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")
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..915e9a8 100644
--- a/erpnext/hr/doctype/employee_separation/employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/employee_separation.py
@@ -1,10 +1,10 @@
-# -*- 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 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 d63501a..f83c1e5 100644
--- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+from frappe.utils import getdate
test_dependencies = ['Employee Onboarding']
@@ -34,9 +34,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',
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..70b84b1 100644
--- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py
+++ b/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py
@@ -1,10 +1,9 @@
-# -*- 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 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 75f985c..6e2a83e 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,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'employee_separation_template',
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..6a0c947 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..13bee34 100644
--- a/erpnext/hr/doctype/employee_skill/employee_skill.py
+++ b/erpnext/hr/doctype/employee_skill/employee_skill.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..ea7da9e 100644
--- a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py
+++ b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..cd92dd6 100644
--- a/erpnext/hr/doctype/employee_training/employee_training.py
+++ b/erpnext/hr/doctype/employee_training/employee_training.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..f927d41 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -1,13 +1,14 @@
-# -*- 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.utils import getdate
-from erpnext.hr.utils import update_employee
+
+from erpnext.hr.utils import update_employee_work_history
+
class EmployeeTransfer(Document):
def before_submit(self):
@@ -21,7 +22,7 @@
new_employee = frappe.copy_doc(employee)
new_employee.name = None
new_employee.employee_number = None
- new_employee = update_employee(new_employee, self.transfer_details, date=self.transfer_date)
+ new_employee = update_employee_work_history(new_employee, self.transfer_details, date=self.transfer_date)
if self.new_company and self.company != self.new_company:
new_employee.internal_work_history = []
new_employee.date_of_joining = self.transfer_date
@@ -36,7 +37,7 @@
employee.db_set("relieving_date", self.transfer_date)
employee.db_set("status", "Left")
else:
- employee = update_employee(employee, self.transfer_details, date=self.transfer_date)
+ employee = update_employee_work_history(employee, self.transfer_details, date=self.transfer_date)
if self.new_company and self.company != self.new_company:
employee.company = self.new_company
employee.date_of_joining = self.transfer_date
@@ -53,7 +54,7 @@
employee.status = "Active"
employee.relieving_date = ''
else:
- employee = update_employee(employee, self.transfer_details, cancel=True)
+ employee = update_employee_work_history(employee, self.transfer_details, date=self.transfer_date, cancel=True)
if self.new_company != self.company:
employee.company = self.company
employee.save()
diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.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..64eee40 100644
--- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
@@ -1,20 +1,24 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import getdate, add_days
+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")
- make_employee("employee3@transfers.com")
- frappe.db.sql("""delete from `tabEmployee Transfer`""")
+ create_company()
+
+ def tearDown(self):
+ frappe.db.rollback()
def test_submit_before_transfer_date(self):
+ make_employee("employee2@transfers.com")
+
transfer_obj = frappe.get_doc({
"doctype": "Employee Transfer",
"employee": frappe.get_value("Employee", {"user_id":"employee2@transfers.com"}, "name"),
@@ -36,6 +40,8 @@
self.assertEqual(transfer.docstatus, 1)
def test_new_employee_creation(self):
+ make_employee("employee3@transfers.com")
+
transfer = frappe.get_doc({
"doctype": "Employee Transfer",
"employee": frappe.get_value("Employee", {"user_id":"employee3@transfers.com"}, "name"),
@@ -54,3 +60,70 @@
self.assertTrue(transfer.new_employee_id)
self.assertEqual(frappe.get_value("Employee", transfer.new_employee_id, "status"), "Active")
self.assertEqual(frappe.get_value("Employee", transfer.employee, "status"), "Left")
+
+ def test_employee_history(self):
+ employee = make_employee("employee4@transfers.com",
+ company="Test Company",
+ date_of_birth=getdate("30-09-1980"),
+ date_of_joining=getdate("01-10-2021"),
+ department="Accounts - TC",
+ designation="Accountant"
+ )
+ transfer = create_employee_transfer(employee)
+
+ count = 0
+ department = ["Accounts - TC", "Management - TC"]
+ designation = ["Accountant", "Manager"]
+ dt = [getdate("01-10-2021"), getdate()]
+
+ employee = frappe.get_doc("Employee", employee)
+ for data in employee.internal_work_history:
+ self.assertEqual(data.department, department[count])
+ self.assertEqual(data.designation, designation[count])
+ self.assertEqual(data.from_date, dt[count])
+ count = count + 1
+
+ transfer.cancel()
+ employee.reload()
+
+ for data in employee.internal_work_history:
+ self.assertEqual(data.designation, designation[0])
+ self.assertEqual(data.department, department[0])
+ self.assertEqual(data.from_date, dt[0])
+
+
+def create_company():
+ if not frappe.db.exists("Company", "Test Company"):
+ frappe.get_doc({
+ "doctype": "Company",
+ "company_name": "Test Company",
+ "default_currency": "INR",
+ "country": "India"
+ }).insert()
+
+
+def create_employee_transfer(employee):
+ doc = frappe.get_doc({
+ "doctype": "Employee Transfer",
+ "employee": employee,
+ "transfer_date": getdate(),
+ "transfer_details": [
+ {
+ "property": "Designation",
+ "current": "Accountant",
+ "new": "Manager",
+ "fieldname": "designation"
+ },
+ {
+ "property": "Department",
+ "current": "Accounts - TC",
+ "new": "Management - TC",
+ "fieldname": "department"
+ }
+ ]
+ })
+
+ doc.save()
+ doc.submit()
+
+ return doc
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py b/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
index 1a665dc..76e2006 100644
--- a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
+++ b/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
@@ -1,10 +1,9 @@
-# -*- 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 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..981d46f 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeTransferProperty(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/employment_type/__init__.py b/erpnext/hr/doctype/employment_type/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/employment_type/__init__.py
+++ b/erpnext/hr/doctype/employment_type/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/employment_type/employment_type.py b/erpnext/hr/doctype/employment_type/employment_type.py
index 00aa6bb..b2262c0 100644
--- a/erpnext/hr/doctype/employment_type/employment_type.py
+++ b/erpnext/hr/doctype/employment_type/employment_type.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class EmploymentType(Document):
pass
diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.py b/erpnext/hr/doctype/employment_type/test_employment_type.py
index 0297ffa..c43f963 100644
--- a/erpnext/hr/doctype/employment_type/test_employment_type.py
+++ b/erpnext/hr/doctype/employment_type/test_employment_type.py
@@ -1,7 +1,6 @@
# 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
+
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..0062ba9
--- /dev/null
+++ b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py
@@ -0,0 +1,10 @@
+# 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 ExpectedSkillSet(Document):
+ pass
diff --git a/erpnext/hr/doctype/expense_claim/__init__.py b/erpnext/hr/doctype/expense_claim/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/expense_claim/__init__.py
+++ b/erpnext/hr/doctype/expense_claim/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 3c4c672..6655563 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;
+ }
+ }
+ });
+ }
},
});
@@ -369,7 +389,9 @@
sanctioned_amount: function(frm, cdt, cdn) {
cur_frm.cscript.calculate_total(frm.doc, cdt, cdn);
frm.trigger("get_taxes");
+ frm.trigger("calculate_grand_total");
},
+
cost_center: function(frm, cdt, cdn) {
erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "expenses", "cost_center");
}
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index a268c15..45b78bf 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.json
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.json
@@ -379,11 +379,12 @@
"idx": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-05-04 05:35:12.040199",
+ "modified": "2021-11-22 16:26:57.787838",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",
"name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index 95e2806..7e3898b 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -1,18 +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 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 +76,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 +88,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 +269,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 fe97350..7539c71 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'reference_name',
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index c2bd1e9..ec70361 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -1,13 +1,14 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import random_string, nowdate
-from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
+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']
@@ -138,6 +139,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 +175,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 +217,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..0d46a22 100644
--- a/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py
+++ b/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class ExpenseClaimAccount(Document):
pass
diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json
index 4550925..aa479c8 100644
--- a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json
+++ b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2017-10-09 16:53:26.410762",
"doctype": "DocType",
"document_type": "Document",
@@ -50,7 +51,7 @@
"fieldname": "unclaimed_amount",
"fieldtype": "Currency",
"in_list_view": 1,
- "label": "Unclaimed amount",
+ "label": "Unclaimed Amount",
"no_copy": 1,
"oldfieldname": "advance_amount",
"oldfieldtype": "Currency",
@@ -65,7 +66,7 @@
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"in_list_view": 1,
- "label": "Allocated amount",
+ "label": "Allocated Amount",
"no_copy": 1,
"oldfieldname": "allocated_amount",
"oldfieldtype": "Currency",
@@ -87,7 +88,7 @@
],
"istable": 1,
"links": [],
- "modified": "2019-12-17 13:53:22.111766",
+ "modified": "2021-11-22 16:33:58.515819",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Advance",
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..68b2963 100644
--- a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py
+++ b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class ExpenseClaimAdvance(Document):
pass
diff --git a/erpnext/hr/doctype/expense_claim_detail/__init__.py b/erpnext/hr/doctype/expense_claim_detail/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/expense_claim_detail/__init__.py
+++ b/erpnext/hr/doctype/expense_claim_detail/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
index 70a48f9..6edbcb5c 100644
--- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
+++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
@@ -94,7 +94,6 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Sanctioned Amount",
- "no_copy": 1,
"oldfieldname": "sanctioned_amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
@@ -120,7 +119,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-11-26 14:23:45.539922",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Detail",
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 5d48990..f58f128 100644
--- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py
+++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class ExpenseClaimDetail(Document):
pass
diff --git a/erpnext/hr/doctype/expense_claim_type/__init__.py b/erpnext/hr/doctype/expense_claim_type/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/expense_claim_type/__init__.py
+++ b/erpnext/hr/doctype/expense_claim_type/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 a637a54..570b2c1 100644
--- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py
+++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py
@@ -1,11 +1,12 @@
# 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 import _
from frappe.model.document import Document
+
class ExpenseClaimType(Document):
def validate(self):
self.validate_accounts()
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..a2403b6 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
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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..2f7b8fc 100644
--- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
+++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
@@ -56,8 +56,6 @@
},
{
"columns": 2,
- "fetch_from": "account_head.tax_rate",
- "fetch_if_empty": 1,
"fieldname": "rate",
"fieldtype": "Float",
"in_list_view": 1,
@@ -102,7 +100,7 @@
],
"istable": 1,
"links": [],
- "modified": "2020-09-23 20:27:36.027728",
+ "modified": "2021-10-26 20:27:36.027728",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Taxes and Charges",
@@ -111,4 +109,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py
index 4103bef..a28ef57 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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/__init__.py b/erpnext/hr/doctype/holiday/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/holiday/__init__.py
+++ b/erpnext/hr/doctype/holiday/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/holiday/holiday.py b/erpnext/hr/doctype/holiday/holiday.py
index 78a95b9..85ca0b7 100644
--- a/erpnext/hr/doctype/holiday/holiday.py
+++ b/erpnext/hr/doctype/holiday/holiday.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class Holiday(Document):
pass
diff --git a/erpnext/hr/doctype/holiday_list/__init__.py b/erpnext/hr/doctype/holiday_list/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/holiday_list/__init__.py
+++ b/erpnext/hr/doctype/holiday_list/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..a8c8c16 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list.py
@@ -1,13 +1,14 @@
-
# 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 +45,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 +92,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 05641c7..4a540ce 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'fieldname': 'holiday_list',
diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.py b/erpnext/hr/doctype/holiday_list/test_holiday_list.py
index 64bed66..c9239ed 100644
--- a/erpnext/hr/doctype/holiday_list/test_holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/test_holiday_list.py
@@ -1,11 +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 unittest
+from datetime import timedelta
import frappe
-import unittest
from frappe.utils import getdate
-from datetime import timedelta
class TestHolidayList(unittest.TestCase):
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js
index ec99472..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')
+ }
+];
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 8aa3c0c..5148435 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -7,30 +7,36 @@
"engine": "InnoDB",
"field_order": [
"employee_settings",
- "retirement_age",
"emp_created_by",
- "column_break_4",
"standard_working_hours",
- "expense_approver_mandatory_in_expense_claim",
+ "column_break_9",
+ "retirement_age",
"reminders_section",
"send_birthday_reminders",
- "column_break_9",
- "send_work_anniversary_reminders",
"column_break_11",
+ "send_work_anniversary_reminders",
+ "column_break_18",
"send_holiday_reminders",
"frequency",
- "leave_settings",
+ "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": [
{
@@ -39,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"
},
{
@@ -63,28 +68,6 @@
"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"
},
@@ -101,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"
},
{
@@ -138,12 +104,41 @@
"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"
@@ -166,6 +161,7 @@
"fieldname": "frequency",
"fieldtype": "Select",
"label": "Set the frequency for holiday reminders",
+ "mandatory_depends_on": "send_holiday_reminders",
"options": "Weekly\nMonthly"
},
{
@@ -181,13 +177,62 @@
{
"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-08-24 14:54:12.834162",
+ "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 a474093..c295bcb 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.py
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.py
@@ -4,7 +4,6 @@
# For license information, please see license.txt
import frappe
-
from frappe.model.document import Document
from frappe.utils import format_date
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..7e13213 100644
--- a/erpnext/hr/doctype/hr_settings/test_hr_settings.py
+++ b/erpnext/hr/doctype/hr_settings/test_hr_settings.py
@@ -1,13 +1,8 @@
-# -*- 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 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..3bfcfaa 100644
--- a/erpnext/hr/doctype/identification_document_type/identification_document_type.py
+++ b/erpnext/hr/doctype/identification_document_type/identification_document_type.py
@@ -1,10 +1,9 @@
-# -*- 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 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..3e8f7ab 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestIdentificationDocumentType(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/interest/interest.py b/erpnext/hr/doctype/interest/interest.py
index 2a9c19c..3563f7f 100644
--- a/erpnext/hr/doctype/interest/interest.py
+++ b/erpnext/hr/doctype/interest/interest.py
@@ -1,10 +1,9 @@
-# -*- 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.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..d4ecd9b 100644
--- a/erpnext/hr/doctype/interest/test_interest.py
+++ b/erpnext/hr/doctype/interest/test_interest.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..4bb003d
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview.py
@@ -0,0 +1,290 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+
+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
+
+
+ if isinstance(data, str):
+ data = frappe._dict(json.loads(data))
+
+ if frappe.session.user != interviewer:
+ frappe.throw(_('Only Interviewer Are allowed to submit Interview Feedback'))
+
+ 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
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..1a2257a
--- /dev/null
+++ b/erpnext/hr/doctype/interview/test_interview.py
@@ -0,0 +1,172 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+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..d44e29a
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/interview_detail.py
@@ -0,0 +1,10 @@
+# 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 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..68a1f72
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/test_interview_detail.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# 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..d046458
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
@@ -0,0 +1,86 @@
+# 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, 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..4185f28
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
@@ -0,0 +1,101 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+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..0f442c3
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/interview_round.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+
+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..dcec941
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/test_interview_round.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+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..f5ebda4
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/interview_type.py
@@ -0,0 +1,10 @@
+# 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 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..96fdfca
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/test_interview_type.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# 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..2dc4a14
--- /dev/null
+++ b/erpnext/hr/doctype/interviewer/interviewer.py
@@ -0,0 +1,10 @@
+# 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 Interviewer(Document):
+ pass
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.js b/erpnext/hr/doctype/job_applicant/job_applicant.js
index 7658bc9..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);
+ }
+ });
}
});
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 14aeb03..abaa50c 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.py
@@ -3,11 +3,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 +27,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,11 +44,43 @@
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))
- if names:
- frappe.throw(_("Email Address must be unique, already exists for {0}").format(comma_and(names)), frappe.DuplicateEntryError)
+ if isinstance(doc, str):
+ 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 ed97978..56331ac 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
@@ -1,15 +1,15 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
- return {
- 'fieldname': 'job_applicant',
- 'transactions': [
- {
- 'items': ['Employee', 'Employee Onboarding']
- },
- {
- 'items': ['Job Offer']
- },
- ],
- }
+ 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.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
index 8728342..36dcf6b 100644
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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 +24,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..1f208c1 100644
--- a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py
+++ b/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py
@@ -1,9 +1,9 @@
-# -*- 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 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..cee5daf 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..39f4719 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.py
+++ b/erpnext/hr/doctype/job_offer/job_offer.py
@@ -1,14 +1,15 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
-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.py b/erpnext/hr/doctype/job_offer/test_job_offer.py
index edb2132..d94e03c 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.py
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.py
@@ -1,12 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, add_days
-from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
+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 +31,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 +43,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)
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..d2eae46 100644
--- a/erpnext/hr/doctype/job_offer_term/job_offer_term.py
+++ b/erpnext/hr/doctype/job_offer_term/job_offer_term.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..d53daf1 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.py
+++ b/erpnext/hr/doctype/job_opening/job_opening.py
@@ -3,12 +3,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 31ef33e..67600dc 100644
--- a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
+++ b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'job_title',
diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.py b/erpnext/hr/doctype/job_opening/test_job_opening.py
index 815ce5b..a1c3a1d 100644
--- a/erpnext/hr/doctype/job_opening/test_job_opening.py
+++ b/erpnext/hr/doctype/job_opening/test_job_opening.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/hr/doctype/leave_allocation/__init__.py
index baffc48..e69de29 100755
--- a/erpnext/hr/doctype/leave_allocation/__init__.py
+++ b/erpnext/hr/doctype/leave_allocation/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
index d947641..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));
}
}
});
+
+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..232118f 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -1,14 +1,19 @@
# 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 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 7a063d9..631beef 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'leave_allocation',
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index fdcd533..46401a2 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -1,10 +1,13 @@
-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/__init__.py b/erpnext/hr/doctype/leave_application/__init__.py
index baffc48..e69de29 100755
--- a/erpnext/hr/doctype/leave_application/__init__.py
+++ b/erpnext/hr/doctype/leave_application/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..1dc5b31 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -1,15 +1,33 @@
# 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 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 +35,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 +75,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 +108,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 +689,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 +728,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 +771,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 31faadb..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: {
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
index c45717f..8b0b98d 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index 2832e2f..f73d3e5 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -1,17 +1,24 @@
# 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 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 +120,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..d6b77f9 100644
--- a/erpnext/hr/doctype/leave_block_list/leave_block_list.py
+++ b/erpnext/hr/doctype/leave_block_list/leave_block_list.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
+
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 45aa491..7cca62e 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,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'fieldname': 'leave_block_list',
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..afbabb6 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
@@ -1,13 +1,14 @@
# 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 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 8e5a09e..50dc125 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
@@ -3,10 +3,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
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 54978a1..36550cc 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
@@ -3,10 +3,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
diff --git a/erpnext/hr/doctype/leave_control_panel/__init__.py b/erpnext/hr/doctype/leave_control_panel/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/leave_control_panel/__init__.py
+++ b/erpnext/hr/doctype/leave_control_panel/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..19f97b8 100644
--- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py
+++ b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py
@@ -1,12 +1,12 @@
# 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 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 +51,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.py b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py
index 9a907c8..d5a9bc0 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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 d136210..8ef0e36 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -1,16 +1,19 @@
-# -*- 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.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):
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..99a479d 100644
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
@@ -1,16 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import today, add_months
+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 33a6243..5c5299e 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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):
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..3121109 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..b1cb688 100644
--- a/erpnext/hr/doctype/leave_period/leave_period.py
+++ b/erpnext/hr/doctype/leave_period/leave_period.py
@@ -1,14 +1,14 @@
-# -*- 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.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 7c2c963..1adae0f 100644
--- a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
+++ b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'leave_period',
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 cbb3437..10936dd 100644
--- a/erpnext/hr/doctype/leave_period/test_leave_period.py
+++ b/erpnext/hr/doctype/leave_period/test_leave_period.py
@@ -1,11 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# 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):
diff --git a/erpnext/hr/doctype/leave_policy/leave_policy.py b/erpnext/hr/doctype/leave_policy/leave_policy.py
index 964a5de..80450d5 100644
--- a/erpnext/hr/doctype/leave_policy/leave_policy.py
+++ b/erpnext/hr/doctype/leave_policy/leave_policy.py
@@ -1,12 +1,12 @@
-# -*- 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
+
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 474f3a7..73782d6 100644
--- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
+++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'leave_policy',
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 af7567b..3dbbef8 100644
--- a/erpnext/hr/doctype/leave_policy/test_leave_policy.py
+++ b/erpnext/hr/doctype/leave_policy/test_leave_policy.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestLeavePolicy(unittest.TestCase):
def test_max_leave_allowed(self):
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..dca7e48 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
@@ -1,15 +1,15 @@
-# -*- 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 import _, bold
-from frappe.utils import getdate, date_diff, comma_and, formatdate, get_datetime, flt
-from math import ceil
+
import json
-from six import string_types
+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
+
class LeavePolicyAssignment(Document):
@@ -137,10 +137,10 @@
@frappe.whitelist()
def create_assignment_for_multiple_employees(employees, data):
- if isinstance(employees, string_types):
+ if isinstance(employees, str):
employees= json.loads(employees)
- if isinstance(data, string_types):
+ if isinstance(data, str):
data = frappe._dict(json.loads(data))
docs_name = []
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 a2f7f58..4363439 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,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'leave_policy_assignment',
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 0089804..b1861ad 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
@@ -1,13 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+
+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"]
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..8916d3d 100644
--- a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py
+++ b/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py
@@ -1,10 +1,9 @@
-# -*- 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 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..aacf64f 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestLeavePolicyDetail(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/leave_type/__init__.py b/erpnext/hr/doctype/leave_type/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/doctype/leave_type/__init__.py
+++ b/erpnext/hr/doctype/leave_type/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..4b59c2c 100644
--- a/erpnext/hr/doctype/leave_type/leave_type.py
+++ b/erpnext/hr/doctype/leave_type/leave_type.py
@@ -1,14 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 c8944fc..074d3e4 100644
--- a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
+++ b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
def get_data():
return {
'fieldname': 'leave_type',
diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py
index 048dddd..c1b64e9 100644
--- a/erpnext/hr/doctype/leave_type/test_leave_type.py
+++ b/erpnext/hr/doctype/leave_type/test_leave_type.py
@@ -1,9 +1,7 @@
# 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 import _
test_records = frappe.get_test_records('Leave Type')
diff --git a/erpnext/hr/doctype/offer_term/offer_term.py b/erpnext/hr/doctype/offer_term/offer_term.py
index 6a63201..cee6c45 100644
--- a/erpnext/hr/doctype/offer_term/offer_term.py
+++ b/erpnext/hr/doctype/offer_term/offer_term.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..2e5ed75 100644
--- a/erpnext/hr/doctype/offer_term/test_offer_term.py
+++ b/erpnext/hr/doctype/offer_term/test_offer_term.py
@@ -1,8 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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..c9d6e71 100644
--- a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py
+++ b/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py
@@ -1,10 +1,9 @@
-# -*- 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 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..354663b 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..5177302 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -1,24 +1,26 @@
-# -*- 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 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):
validate_active_employee(self.employee)
self.validate_overlapping_dates()
- if self.end_date and self.end_date <= self.start_date:
- frappe.throw(_("End Date must not be lesser than Start Date"))
+ if self.end_date:
+ self.validate_from_to_dates('start_date', 'end_date')
def validate_overlapping_dates(self):
if not self.name:
@@ -135,7 +137,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 +145,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 +200,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 +222,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 +236,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/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 07d92fe..d490081 100644
--- a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, add_days
+from frappe.utils import add_days, nowdate
test_dependencies = ["Shift Type"]
diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py
index 2731da1..d4fcf99 100644
--- a/erpnext/hr/doctype/shift_request/shift_request.py
+++ b/erpnext/hr/doctype/shift_request/shift_request.py
@@ -1,14 +1,15 @@
-# -*- 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.utils import formatdate, getdate
+
from erpnext.hr.utils import share_doc_with_approver, validate_active_employee
+
class OverlapError(frappe.ValidationError): pass
class ShiftRequest(Document):
diff --git a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
index f70b61a..531c98d 100644
--- a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
+++ b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'shift_request',
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 60b7676..3633c9b 100644
--- a/erpnext/hr/doctype/shift_request/test_shift_request.py
+++ b/erpnext/hr/doctype/shift_request/test_shift_request.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, add_days
+from frappe.utils import add_days, nowdate
+
from erpnext.hr.doctype.employee.test_employee import make_employee
test_dependencies = ["Shift Type"]
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index d5fdda8..562a573 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -1,18 +1,25 @@
-# -*- 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 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()
@@ -88,7 +95,7 @@
assigned_employees = [x[0] for x in assigned_employees]
if consider_default_shift:
- filters = {'default_shift': self.name}
+ filters = {'default_shift': self.name, 'status': ['!=', 'Inactive']}
default_shift_employees = frappe.get_all('Employee', 'name', filters, as_list=True)
default_shift_employees = [x[0] for x in default_shift_employees]
return list(set(assigned_employees+default_shift_employees))
diff --git a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
index aedd190..919da2d 100644
--- a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
+++ b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'shift',
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..7d2f29c 100644
--- a/erpnext/hr/doctype/shift_type/test_shift_type.py
+++ b/erpnext/hr/doctype/shift_type/test_shift_type.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestShiftType(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/skill/skill.py b/erpnext/hr/doctype/skill/skill.py
index 8d24212..d26e7ca 100644
--- a/erpnext/hr/doctype/skill/skill.py
+++ b/erpnext/hr/doctype/skill/skill.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..13775be
--- /dev/null
+++ b/erpnext/hr/doctype/skill_assessment/skill_assessment.py
@@ -0,0 +1,10 @@
+# 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 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..7b2ea21 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -1,14 +1,14 @@
-# -*- 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
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 +153,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 8e89d53..abde0d5 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'staffing_plan',
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 1c6218e..8ff0dbb 100644
--- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
@@ -1,13 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+from frappe.utils import add_days, nowdate
+
+from erpnext.hr.doctype.staffing_plan.staffing_plan import (
+ ParentCompanyError,
+ SubsidiaryCompanyError,
+)
test_dependencies = ["Designation"]
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..6749690 100644
--- a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py
+++ b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py
@@ -1,10 +1,9 @@
-# -*- 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 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 6a275b3..f4329c9 100644
--- a/erpnext/hr/doctype/training_event/test_training_event.py
+++ b/erpnext/hr/doctype/training_event/test_training_event.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import today, add_days
+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")
diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py
index e2c30cb..c8c8bbe 100644
--- a/erpnext/hr/doctype/training_event/training_event.py
+++ b/erpnext/hr/doctype/training_event/training_event.py
@@ -1,14 +1,15 @@
-# -*- 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.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 19afd8d..141fffc 100644
--- a/erpnext/hr/doctype/training_event/training_event_dashboard.py
+++ b/erpnext/hr/doctype/training_event/training_event_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'training_event',
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..5dce6e1 100644
--- a/erpnext/hr/doctype/training_event_employee/training_event_employee.py
+++ b/erpnext/hr/doctype/training_event_employee/training_event_employee.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class TrainingEventEmployee(Document):
pass
diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.py b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
index 4c0c180..58ed623 100644
--- a/erpnext/hr/doctype/training_feedback/test_training_feedback.py
+++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
@@ -1,12 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
+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")
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py
index 3d4b9b3..1f9ec3b 100644
--- a/erpnext/hr/doctype/training_feedback/training_feedback.py
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
from frappe import _
+from frappe.model.document import Document
+
class TrainingFeedback(Document):
def validate(self):
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..5000705 100644
--- a/erpnext/hr/doctype/training_program/test_training_program.py
+++ b/erpnext/hr/doctype/training_program/test_training_program.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestTrainingProgram(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/training_program/training_program.py b/erpnext/hr/doctype/training_program/training_program.py
index 7a3720b..96b2fd7 100644
--- a/erpnext/hr/doctype/training_program/training_program.py
+++ b/erpnext/hr/doctype/training_program/training_program.py
@@ -1,9 +1,9 @@
-# -*- 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 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 0fc18a8..374c1e8 100644
--- a/erpnext/hr/doctype/training_program/training_program_dashboard.py
+++ b/erpnext/hr/doctype/training_program/training_program_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'training_program',
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..1735ff4 100644
--- a/erpnext/hr/doctype/training_result/test_training_result.py
+++ b/erpnext/hr/doctype/training_result/test_training_result.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 5cdbcad..718b383 100644
--- a/erpnext/hr/doctype/training_result/training_result.js
+++ b/erpnext/hr/doctype/training_result/training_result.js
@@ -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..bb5c71e 100644
--- a/erpnext/hr/doctype/training_result/training_result.py
+++ b/erpnext/hr/doctype/training_result/training_result.py
@@ -1,13 +1,14 @@
-# -*- 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.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/training_result_employee.py b/erpnext/hr/doctype/training_result_employee/training_result_employee.py
index 54e2a18..e048ff5 100644
--- a/erpnext/hr/doctype/training_result_employee/training_result_employee.py
+++ b/erpnext/hr/doctype/training_result_employee/training_result_employee.py
@@ -1,10 +1,9 @@
-# -*- 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.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..529909b 100644
--- a/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py
+++ b/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py
@@ -1,10 +1,9 @@
-# -*- 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 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..e29a1ca 100644
--- a/erpnext/hr/doctype/travel_request/test_travel_request.py
+++ b/erpnext/hr/doctype/travel_request/test_travel_request.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..02379c5 100644
--- a/erpnext/hr/doctype/travel_request/travel_request.py
+++ b/erpnext/hr/doctype/travel_request/travel_request.py
@@ -1,12 +1,12 @@
-# -*- 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
+
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..0d1a592 100644
--- a/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py
+++ b/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py
@@ -1,10 +1,9 @@
-# -*- 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 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..4c7bd80 100644
--- a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
@@ -1,14 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-import erpnext
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 9c765d7..94eb300 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
@@ -3,15 +3,17 @@
# 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_holiday_dates_for_employee
+
class UploadAttendance(Document):
pass
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..c5ea5a3 100644
--- a/erpnext/hr/doctype/vehicle/test_vehicle.py
+++ b/erpnext/hr/doctype/vehicle/test_vehicle.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate,flt, cstr,random_string
+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..946233b 100644
--- a/erpnext/hr/doctype/vehicle/vehicle.py
+++ b/erpnext/hr/doctype/vehicle/vehicle.py
@@ -1,12 +1,12 @@
-# -*- 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 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 628c897..f6e5f06 100644
--- a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
+++ b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
index ed02120..acd50f2 100644
--- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
+++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, flt, cstr, random_string
+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'""")
diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py
index 04c94e3..e414141 100644
--- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py
+++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py
@@ -1,13 +1,12 @@
-# -*- 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, 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..fdd4e39 100644
--- a/erpnext/hr/doctype/vehicle_service/vehicle_service.py
+++ b/erpnext/hr/doctype/vehicle_service/vehicle_service.py
@@ -1,10 +1,9 @@
-# -*- 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.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.py b/erpnext/hr/notification/training_feedback/training_feedback.py
index 2334f8b..02e3e93 100644
--- a/erpnext/hr/notification/training_feedback/training_feedback.py
+++ b/erpnext/hr/notification/training_feedback/training_feedback.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.py b/erpnext/hr/notification/training_scheduled/training_scheduled.py
index 2334f8b..02e3e93 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.py
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
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/hr/page/__init__.py b/erpnext/hr/page/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/hr/page/__init__.py
+++ b/erpnext/hr/page/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js
index 81162a4..b0e41e0 100644
--- a/erpnext/hr/page/organizational_chart/organizational_chart.js
+++ b/erpnext/hr/page/organizational_chart/organizational_chart.js
@@ -15,6 +15,8 @@
} 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.py b/erpnext/hr/page/organizational_chart/organizational_chart.py
index 4423d29..1e2d758 100644
--- a/erpnext/hr/page/organizational_chart/organizational_chart.py
+++ b/erpnext/hr/page/organizational_chart/organizational_chart.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
@frappe.whitelist()
def get_children(parent=None, company=None, exclude_node=None):
filters = [['status', '!=', 'Left']]
diff --git a/erpnext/hr/page/team_updates/team_updates.py b/erpnext/hr/page/team_updates/team_updates.py
index 58cdc4b..0a4624c 100644
--- a/erpnext/hr/page/team_updates/team_updates.py
+++ b/erpnext/hr/page/team_updates/team_updates.py
@@ -1,8 +1,7 @@
-from __future__ import unicode_literals
-
import frappe
from email_reply_parser import EmailReplyParser
+
@frappe.whitelist()
def get_data(start=0):
#frappe.only_for('Employee', 'System Manager')
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 d8691b4..63764bb 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
@@ -1,11 +1,13 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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)
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..62b83f2 100644
--- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py
+++ b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py
@@ -1,9 +1,10 @@
# 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 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 fe77b6a..3a75276 100644
--- a/erpnext/hr/report/employee_analytics/employee_analytics.py
+++ b/erpnext/hr/report/employee_analytics/employee_analytics.py
@@ -1,10 +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):
if not filters: filters = {}
diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.py b/erpnext/hr/report/employee_birthday/employee_birthday.py
index e8d7844..cec5a48 100644
--- a/erpnext/hr/report/employee_birthday/employee_birthday.py
+++ b/erpnext/hr/report/employee_birthday/employee_birthday.py
@@ -1,10 +1,10 @@
# 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 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..b375b18 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -1,13 +1,19 @@
# 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 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"'))
@@ -175,10 +181,11 @@
records= frappe.db.sql("""
SELECT
employee, leave_type, from_date, to_date, leaves, transaction_name,
- is_carry_forward, is_expired
+ transaction_type, is_carry_forward, is_expired
FROM `tabLeave Ledger Entry`
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
AND docstatus=1
+ AND transaction_type = 'Leave Allocation'
AND (from_date between %(from_date)s AND %(to_date)s
OR to_date between %(from_date)s AND %(to_date)s
OR (from_date < %(from_date)s AND to_date > %(to_date)s))
diff --git a/erpnext/hr/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..71c18bb 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
@@ -1,15 +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
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 +65,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..00a4a7c 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
@@ -1,7 +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 _
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..9a993e5 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
@@ -1,12 +1,13 @@
# 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 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.py b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
index 303c829..6383a9b 100644
--- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
+++ b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
@@ -1,10 +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):
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..8672e68 100644
--- a/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
@@ -1,15 +1,18 @@
# 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
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.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
index d847cbb..17d1e9d 100644
--- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
@@ -1,13 +1,14 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 +97,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 15b237d..0febce1 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
@@ -16,10 +29,21 @@
if doc.employee and not doc.employee_name:
doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name")
-def update_employee(employee, details, date=None, cancel=False):
+def update_employee_work_history(employee, details, date=None, cancel=False):
+ if not employee.internal_work_history and not cancel:
+ employee.append("internal_work_history", {
+ "branch": employee.branch,
+ "designation": employee.designation,
+ "department": employee.department,
+ "from_date": employee.date_of_joining
+ })
+
internal_work_history = {}
for item in details:
- 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)
@@ -28,11 +52,35 @@
setattr(employee, item.fieldname, new_data)
if item.fieldname in ["department", "designation", "branch"]:
internal_work_history[item.fieldname] = item.new
+
if internal_work_history and not cancel:
internal_work_history["from_date"] = date
employee.append("internal_work_history", internal_work_history)
+
+ if cancel:
+ delete_employee_work_history(details, employee, date)
+
return employee
+def delete_employee_work_history(details, employee, date):
+ filters = {}
+ for d in details:
+ for history in employee.internal_work_history:
+ if d.property == "Department" and history.department == d.new:
+ department = d.new
+ filters["department"] = department
+ if d.property == "Designation" and history.designation == d.new:
+ designation = d.new
+ filters["designation"] = designation
+ if d.property == "Branch" and history.branch == d.new:
+ branch = d.new
+ filters["branch"] = branch
+ if date and date == history.from_date:
+ filters["from_date"] = date
+ if filters:
+ frappe.db.delete("Employee Internal Work History", filters)
+
+
@frappe.whitelist()
def get_employee_fields_label():
fields = []
@@ -272,6 +320,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)
@@ -337,9 +386,9 @@
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)
-
+ # return only date
+ holidays = get_holidays_for_employee(employee, start_date, end_date)
+
return [cstr(h.holiday_date) for h in holidays]
@@ -352,7 +401,7 @@
`raise_exception` (bool)
`only_non_weekly` (bool)
- return: list of dicts with `holiday_date` and `description`
+ return: list of dicts with `holiday_date` and `description`
"""
holiday_list = get_holiday_list_for_employee(employee, raise_exception=raise_exception)
@@ -368,11 +417,11 @@
filters['weekly_off'] = False
holidays = frappe.get_all(
- 'Holiday',
+ 'Holiday',
fields=['description', 'holiday_date'],
filters=filters
)
-
+
return holidays
@erpnext.allow_regional
diff --git a/erpnext/hr/web_form/job_application/job_application.py b/erpnext/hr/web_form/job_application/job_application.py
index 2334f8b..02e3e93 100644
--- a/erpnext/hr/web_form/job_application/job_application.py
+++ b/erpnext/hr/web_form/job_application/job_application.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index 575fa7b..7408d63 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -1,5 +1,4 @@
{
- "category": "",
"charts": [
{
"chart_name": "Outgoing Salary",
@@ -8,18 +7,12 @@
],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Human Resource\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Employee\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Leave Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Job Applicant\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee Lifecycle\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Shift Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Leaves\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Expense Claims\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fleet Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Recruitment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loans\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Training\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Performance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:48:58.322521",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "hr",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "HR",
"links": [
{
@@ -224,6 +217,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,15 +935,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:59.842918",
+ "modified": "2021-08-31 12:18:59.842919",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",
- "onboarding": "Human Resource",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/hub/__init__.py b/erpnext/hub/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py
deleted file mode 100644
index 85ffe29..0000000
--- a/erpnext/hub_node/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-@frappe.whitelist()
-def enable_hub():
- hub_settings = frappe.get_doc('Marketplace Settings')
- hub_settings.register()
- frappe.db.commit()
- return hub_settings
-
-@frappe.whitelist()
-def sync():
- hub_settings = frappe.get_doc('Marketplace Settings')
- hub_settings.sync()
diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py
deleted file mode 100644
index 42f9000..0000000
--- a/erpnext/hub_node/api.py
+++ /dev/null
@@ -1,233 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-import json
-
-from frappe import _
-from frappe.frappeclient import FrappeClient
-from frappe.desk.form.load import get_attachments
-from six import string_types
-
-current_user = frappe.session.user
-
-
-@frappe.whitelist()
-def register_marketplace(company, company_description):
- validate_registerer()
-
- settings = frappe.get_single('Marketplace Settings')
- message = settings.register_seller(company, company_description)
-
- if message.get('hub_seller_name'):
- settings.registered = 1
- settings.hub_seller_name = message.get('hub_seller_name')
- settings.save()
-
- settings.add_hub_user(frappe.session.user)
-
- return { 'ok': 1 }
-
-
-@frappe.whitelist()
-def register_users(user_list):
- user_list = json.loads(user_list)
-
- settings = frappe.get_single('Marketplace Settings')
-
- for user in user_list:
- settings.add_hub_user(user)
-
- return user_list
-
-
-def validate_registerer():
- if current_user == 'Administrator':
- frappe.throw(_('Please login as another user to register on Marketplace'))
-
- valid_roles = ['System Manager', 'Item Manager']
-
- if not frappe.utils.is_subset(valid_roles, frappe.get_roles()):
- frappe.throw(_('Only users with {0} role can register on Marketplace').format(', '.join(valid_roles)),
- frappe.PermissionError)
-
-
-@frappe.whitelist()
-def call_hub_method(method, params=None):
- connection = get_hub_connection()
-
- if isinstance(params, string_types):
- params = json.loads(params)
-
- params.update({
- 'cmd': 'hub.hub.api.' + method
- })
-
- response = connection.post_request(params)
- return response
-
-
-def map_fields(items):
- field_mappings = get_field_mappings()
- table_fields = [d.fieldname for d in frappe.get_meta('Item').get_table_fields()]
-
- hub_seller_name = frappe.db.get_value('Marketplace Settings', 'Marketplace Settings', 'hub_seller_name')
-
- for item in items:
- for fieldname in table_fields:
- item.pop(fieldname, None)
-
- for mapping in field_mappings:
- local_fieldname = mapping.get('local_fieldname')
- remote_fieldname = mapping.get('remote_fieldname')
-
- value = item.get(local_fieldname)
- item.pop(local_fieldname, None)
- item[remote_fieldname] = value
-
- item['doctype'] = 'Hub Item'
- item['hub_seller'] = hub_seller_name
- item.pop('attachments', None)
-
- return items
-
-
-@frappe.whitelist()
-def get_valid_items(search_value=''):
- items = frappe.get_list(
- 'Item',
- fields=["*"],
- filters={
- 'disabled': 0,
- 'item_name': ['like', '%' + search_value + '%'],
- 'publish_in_hub': 0
- },
- order_by="modified desc"
- )
-
- valid_items = filter(lambda x: x.image and x.description, items)
-
- def prepare_item(item):
- item.source_type = "local"
- item.attachments = get_attachments('Item', item.item_code)
- return item
-
- valid_items = map(prepare_item, valid_items)
-
- return valid_items
-
-@frappe.whitelist()
-def update_item(ref_doc, data):
- data = json.loads(data)
-
- data.update(dict(doctype='Hub Item', name=ref_doc))
- try:
- connection = get_hub_connection()
- connection.update(data)
- except Exception as e:
- frappe.log_error(message=e, title='Hub Sync Error')
-
-@frappe.whitelist()
-def publish_selected_items(items_to_publish):
- items_to_publish = json.loads(items_to_publish)
- items_to_update = []
- if not len(items_to_publish):
- frappe.throw(_('No items to publish'))
-
- for item in items_to_publish:
- item_code = item.get('item_code')
- frappe.db.set_value('Item', item_code, 'publish_in_hub', 1)
-
- hub_dict = {
- 'doctype': 'Hub Tracked Item',
- 'item_code': item_code,
- 'published': 1,
- 'hub_category': item.get('hub_category'),
- 'image_list': item.get('image_list')
- }
- frappe.get_doc(hub_dict).insert(ignore_if_duplicate=True)
-
- items = map_fields(items_to_publish)
-
- try:
- item_sync_preprocess(len(items))
- convert_relative_image_urls_to_absolute(items)
-
- # TODO: Publish Progress
- connection = get_hub_connection()
- connection.insert_many(items)
-
- item_sync_postprocess()
- except Exception as e:
- frappe.log_error(message=e, title='Hub Sync Error')
-
-@frappe.whitelist()
-def unpublish_item(item_code, hub_item_name):
- ''' Remove item listing from the marketplace '''
-
- response = call_hub_method('unpublish_item', {
- 'hub_item_name': hub_item_name
- })
-
- if response:
- frappe.db.set_value('Item', item_code, 'publish_in_hub', 0)
- frappe.delete_doc('Hub Tracked Item', item_code)
- else:
- frappe.throw(_('Unable to update remote activity'))
-
-@frappe.whitelist()
-def get_unregistered_users():
- settings = frappe.get_single('Marketplace Settings')
- registered_users = [user.user for user in settings.users] + ['Administrator', 'Guest']
- all_users = [user.name for user in frappe.db.get_all('User', filters={'enabled': 1})]
- unregistered_users = [user for user in all_users if user not in registered_users]
- return unregistered_users
-
-
-def item_sync_preprocess(intended_item_publish_count):
- response = call_hub_method('pre_items_publish', {
- 'intended_item_publish_count': intended_item_publish_count
- })
-
- if response:
- frappe.db.set_value("Marketplace Settings", "Marketplace Settings", "sync_in_progress", 1)
- return response
- else:
- frappe.throw(_('Unable to update remote activity'))
-
-
-def item_sync_postprocess():
- response = call_hub_method('post_items_publish', {})
- if response:
- frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'last_sync_datetime', frappe.utils.now())
- else:
- frappe.throw(_('Unable to update remote activity'))
-
- frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'sync_in_progress', 0)
-
-
-def convert_relative_image_urls_to_absolute(items):
- from six.moves.urllib.parse import urljoin
-
- for item in items:
- file_path = item['image']
-
- if file_path.startswith('/files/'):
- item['image'] = urljoin(frappe.utils.get_url(), file_path)
-
-
-def get_hub_connection():
- settings = frappe.get_single('Marketplace Settings')
- marketplace_url = settings.marketplace_url
- hub_user = settings.get_hub_user(frappe.session.user)
-
- if hub_user:
- password = hub_user.get_password()
- hub_connection = FrappeClient(marketplace_url, hub_user.user, password)
- return hub_connection
- else:
- read_only_hub_connection = FrappeClient(marketplace_url)
- return read_only_hub_connection
-
-
-def get_field_mappings():
- return []
diff --git a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json b/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json
deleted file mode 100644
index b1e421d..0000000
--- a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "condition": "{'name': ('=', frappe.db.get_single_value('Hub Settings', 'company'))}",
- "creation": "2017-09-07 11:38:43.169065",
- "docstatus": 0,
- "doctype": "Data Migration Mapping",
- "fields": [
- {
- "is_child_table": 0,
- "local_fieldname": "name",
- "remote_fieldname": "company_name"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "country",
- "remote_fieldname": "country"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "\"city\"",
- "remote_fieldname": "seller_city"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "eval:frappe.local.site",
- "remote_fieldname": "site_name"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "eval:frappe.session.user",
- "remote_fieldname": "user"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "company_logo",
- "remote_fieldname": "company_logo"
- }
- ],
- "idx": 2,
- "local_doctype": "Company",
- "mapping_name": "Company to Hub Company",
- "mapping_type": "Push",
- "migration_id_field": "hub_sync_id",
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "name": "Company to Hub Company",
- "owner": "Administrator",
- "page_length": 10,
- "remote_objectname": "Hub Company",
- "remote_primary_key": "name"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json b/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json
deleted file mode 100644
index d11abeb..0000000
--- a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "condition": "{'reference_doctype': 'Lead', 'user': frappe.db.get_single_value('Hub Settings', 'user'), 'status': 'Pending'}",
- "creation": "2017-09-20 15:06:40.279930",
- "docstatus": 0,
- "doctype": "Data Migration Mapping",
- "fields": [
- {
- "is_child_table": 0,
- "local_fieldname": "email_id",
- "remote_fieldname": "email_id"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "lead_name",
- "remote_fieldname": "lead_name"
- }
- ],
- "idx": 0,
- "local_doctype": "Lead",
- "local_primary_key": "email_id",
- "mapping_name": "Hub Message to Lead",
- "mapping_type": "Pull",
- "migration_id_field": "hub_sync_id",
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "name": "Hub Message to Lead",
- "owner": "Administrator",
- "page_length": 10,
- "remote_objectname": "Hub Message",
- "remote_primary_key": "name"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json
deleted file mode 100644
index bcece69..0000000
--- a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "condition": "{\"publish_in_hub\": 1}",
- "creation": "2017-09-07 13:27:52.726350",
- "docstatus": 0,
- "doctype": "Data Migration Mapping",
- "fields": [
- {
- "is_child_table": 0,
- "local_fieldname": "item_code",
- "remote_fieldname": "item_code"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "item_name",
- "remote_fieldname": "item_name"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "eval:frappe.db.get_value('Hub Settings' , 'Hub Settings', 'company_email')",
- "remote_fieldname": "hub_seller"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "image",
- "remote_fieldname": "image"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "image_list",
- "remote_fieldname": "image_list"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "item_group",
- "remote_fieldname": "item_group"
- },
- {
- "is_child_table": 0,
- "local_fieldname": "hub_category",
- "remote_fieldname": "hub_category"
- }
- ],
- "idx": 1,
- "local_doctype": "Item",
- "mapping_name": "Item to Hub Item",
- "mapping_type": "Push",
- "migration_id_field": "hub_sync_id",
- "modified": "2018-08-19 22:20:25.727581",
- "modified_by": "Administrator",
- "name": "Item to Hub Item",
- "owner": "Administrator",
- "page_length": 10,
- "remote_objectname": "Hub Item",
- "remote_primary_key": "item_code"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json b/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json
deleted file mode 100644
index e90b1dd..0000000
--- a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "creation": "2017-09-07 11:39:38.445902",
- "docstatus": 0,
- "doctype": "Data Migration Plan",
- "idx": 1,
- "mappings": [
- {
- "enabled": 1,
- "mapping": "Item to Hub Item"
- }
- ],
- "modified": "2018-08-19 22:20:25.644602",
- "modified_by": "Administrator",
- "module": "Hub Node",
- "name": "Hub Sync",
- "owner": "Administrator",
- "plan_name": "Hub Sync",
- "postprocess_method": "erpnext.hub_node.api.item_sync_postprocess"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/__init__.py b/erpnext/hub_node/doctype/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/__init__.py b/erpnext/hub_node/doctype/hub_tracked_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js
deleted file mode 100644
index 660532d..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Hub Tracked Item', {
- refresh: function(frm) {
-
- }
-});
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json
deleted file mode 100644
index 7d07ba4..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json
+++ /dev/null
@@ -1,210 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "field:item_code",
- "beta": 0,
- "creation": "2018-03-18 09:33:50.267762",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_code",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Item Code",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 1
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "hub_category",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Hub Category",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "published",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Published",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "image_list",
- "fieldtype": "Long Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Image List",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-12-10 11:37:35.951019",
- "modified_by": "Administrator",
- "module": "Hub Node",
- "name": "Hub Tracked Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Item Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 1,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py
deleted file mode 100644
index be2cd6b..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.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 HubTrackedItem(Document):
- pass
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py
deleted file mode 100644
index 92b2940..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestHubTrackedItem(unittest.TestCase):
- pass
diff --git a/erpnext/hub_node/doctype/hub_user/__init__.py b/erpnext/hub_node/doctype/hub_user/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/hub_user/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.json b/erpnext/hub_node/doctype/hub_user/hub_user.json
deleted file mode 100644
index f51ffb4..0000000
--- a/erpnext/hub_node/doctype/hub_user/hub_user.json
+++ /dev/null
@@ -1,140 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "",
- "beta": 0,
- "creation": "2018-08-31 12:36:45.627531",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "user",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "User",
- "length": 0,
- "no_copy": 0,
- "options": "User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 1
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "hub_user_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Hub User",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "password",
- "fieldtype": "Password",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Hub Password",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "module": "Hub Node",
- "name": "Hub User",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.py b/erpnext/hub_node/doctype/hub_user/hub_user.py
deleted file mode 100644
index de43f4e..0000000
--- a/erpnext/hub_node/doctype/hub_user/hub_user.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 HubUser(Document):
- pass
diff --git a/erpnext/hub_node/doctype/hub_users/__init__.py b/erpnext/hub_node/doctype/hub_users/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/hub_users/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.json b/erpnext/hub_node/doctype/hub_users/hub_users.json
deleted file mode 100644
index d42f3fd..0000000
--- a/erpnext/hub_node/doctype/hub_users/hub_users.json
+++ /dev/null
@@ -1,72 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-03-06 04:38:49.891787",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "user",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "User",
- "length": 0,
- "no_copy": 0,
- "options": "User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "module": "Hub Node",
- "name": "Hub Users",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.py b/erpnext/hub_node/doctype/hub_users/hub_users.py
deleted file mode 100644
index 440be14..0000000
--- a/erpnext/hub_node/doctype/hub_users/hub_users.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 HubUsers(Document):
- pass
diff --git a/erpnext/hub_node/doctype/marketplace_settings/__init__.py b/erpnext/hub_node/doctype/marketplace_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js
deleted file mode 100644
index 36da832..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Marketplace Settings', {
- refresh: function(frm) {
- $('#toolbar-user .marketplace-link').toggle(!frm.doc.disable_marketplace);
- },
-});
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
deleted file mode 100644
index e784f68..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
+++ /dev/null
@@ -1,410 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 1,
- "creation": "2018-08-31 15:54:38.795263",
- "custom": 0,
- "description": "",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 0,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "disable_marketplace",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disable Marketplace",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:!doc.disable_marketplace",
- "fieldname": "marketplace_settings_section",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Marketplace Settings",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "https://hubmarket.org",
- "fieldname": "marketplace_url",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Marketplace URL (to hide and update label)",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "registered",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Registered",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sync_in_progress",
- "fieldtype": "Check",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sync in Progress",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "hub_seller_name",
- "fieldtype": "Data",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Hub Seller Name",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "users",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Users",
- "length": 0,
- "no_copy": 0,
- "options": "Hub User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "last_sync_datetime",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Last Sync On",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "depends_on": "eval:1",
- "fieldname": "custom_data",
- "fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Custom Data",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "module": "Hub Node",
- "name": "Marketplace Settings",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 0,
- "role": "All",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
- }
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py
deleted file mode 100644
index 91c7bf5..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import 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 _
-from frappe.frappeclient import FrappeClient
-from erpnext.utilities.product import get_price, get_qty_in_stock
-from six import string_types
-
-class MarketplaceSettings(Document):
-
- def register_seller(self, company, company_description):
-
- country, currency, company_logo = frappe.db.get_value('Company', company,
- ['country', 'default_currency', 'company_logo'])
-
- company_details = {
- 'company': company,
- 'country': country,
- 'currency': currency,
- 'company_description': company_description,
- 'company_logo': company_logo,
- 'site_name': frappe.utils.get_url()
- }
-
- hub_connection = self.get_connection()
-
- response = hub_connection.post_request({
- 'cmd': 'hub.hub.api.add_hub_seller',
- 'company_details': json.dumps(company_details)
- })
-
- return response
-
-
- def add_hub_user(self, user_email):
- '''Create a Hub User and User record on hub server
- and if successfull append it to Hub User table
- '''
-
- if not self.registered:
- return
-
- hub_connection = self.get_connection()
-
- first_name, last_name = frappe.db.get_value('User', user_email, ['first_name', 'last_name'])
-
- hub_user = hub_connection.post_request({
- 'cmd': 'hub.hub.api.add_hub_user',
- 'user_email': user_email,
- 'first_name': first_name,
- 'last_name': last_name,
- 'hub_seller': self.hub_seller_name
- })
-
- self.append('users', {
- 'user': hub_user.get('user_email'),
- 'hub_user_name': hub_user.get('hub_user_name'),
- 'password': hub_user.get('password')
- })
-
- self.save()
-
- def get_hub_user(self, user):
- '''Return the Hub User doc from the `users` table if password is set'''
-
- filtered_users = list(filter(
- lambda x: x.user == user and x.password,
- self.users
- ))
-
- if filtered_users:
- return filtered_users[0]
-
-
- def get_connection(self):
- return FrappeClient(self.marketplace_url)
-
-
- def unregister(self):
- """Disable the User on hubmarket.org"""
- pass
-
-@frappe.whitelist()
-def is_marketplace_enabled():
- if not hasattr(frappe.local, 'is_marketplace_enabled'):
- frappe.local.is_marketplace_enabled = cint(frappe.db.get_single_value('Marketplace Settings',
- 'disable_marketplace'))
-
- return frappe.local.is_marketplace_enabled
diff --git a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.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
deleted file mode 100644
index 549b991..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestMarketplaceSettings(unittest.TestCase):
- pass
diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py
deleted file mode 100644
index b61b88b..0000000
--- a/erpnext/hub_node/legacy.py
+++ /dev/null
@@ -1,144 +0,0 @@
-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
-from frappe.contacts.doctype.contact.contact import get_default_contact
-
-def get_list(doctype, start, limit, fields, filters, order_by):
- pass
-
-def get_hub_connection():
- if frappe.db.exists('Data Migration Connector', 'Hub Connector'):
- hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector')
- hub_connection = hub_connector.get_connection()
- return hub_connection.connection
-
- # read-only connection
- hub_connection = FrappeClient(frappe.conf.hub_url)
- return hub_connection
-
-def make_opportunity(buyer_name, email_id):
- buyer_name = "HUB-" + buyer_name
-
- if not frappe.db.exists('Lead', {'email_id': email_id}):
- lead = frappe.new_doc("Lead")
- lead.lead_name = buyer_name
- lead.email_id = email_id
- lead.save(ignore_permissions=True)
-
- o = frappe.new_doc("Opportunity")
- o.opportunity_from = "Lead"
- o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"]
- o.save(ignore_permissions=True)
-
-@frappe.whitelist()
-def make_rfq_and_send_opportunity(item, supplier):
- supplier = make_supplier(supplier)
- contact = make_contact(supplier)
- item = make_item(item)
- rfq = make_rfq(item, supplier, contact)
- status = send_opportunity(contact)
-
- return {
- 'rfq': rfq,
- 'hub_document_created': status
- }
-
-def make_supplier(supplier):
- # make supplier if not already exists
- supplier = frappe._dict(json.loads(supplier))
-
- if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}):
- supplier_doc = frappe.get_doc({
- 'doctype': 'Supplier',
- 'supplier_name': supplier.supplier_name,
- 'supplier_group': supplier.supplier_group,
- 'supplier_email': supplier.supplier_email
- }).insert()
- else:
- supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name)
-
- return supplier_doc
-
-def make_contact(supplier):
- contact_name = get_default_contact('Supplier', supplier.supplier_name)
- # make contact if not already exists
- if not contact_name:
- contact = frappe.get_doc({
- 'doctype': 'Contact',
- 'first_name': supplier.supplier_name,
- 'is_primary_contact': 1,
- 'links': [
- {'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
- ]
- })
- contact.add_email(supplier.supplier_email, is_primary=True)
- contact.insert()
- else:
- contact = frappe.get_doc('Contact', contact_name)
-
- return contact
-
-def make_item(item):
- # make item if not already exists
- item = frappe._dict(json.loads(item))
-
- if not frappe.db.exists('Item', {'item_code': item.item_code}):
- item_doc = frappe.get_doc({
- 'doctype': 'Item',
- 'item_code': item.item_code,
- 'item_group': item.item_group,
- 'is_item_from_hub': 1
- }).insert()
- else:
- item_doc = frappe.get_doc('Item', item.item_code)
-
- return item_doc
-
-def make_rfq(item, supplier, contact):
- # make rfq
- rfq = frappe.get_doc({
- 'doctype': 'Request for Quotation',
- 'transaction_date': nowdate(),
- 'status': 'Draft',
- 'company': frappe.db.get_single_value('Marketplace Settings', 'company'),
- 'message_for_supplier': 'Please supply the specified items at the best possible rates',
- 'suppliers': [
- { 'supplier': supplier.name, 'contact': contact.name }
- ],
- 'items': [
- {
- 'item_code': item.item_code,
- 'qty': 1,
- 'schedule_date': nowdate(),
- 'warehouse': item.default_warehouse or get_root_of("Warehouse"),
- 'description': item.description,
- 'uom': item.stock_uom
- }
- ]
- }).insert()
-
- rfq.save()
- rfq.submit()
- return rfq
-
-def send_opportunity(contact):
- # Make Hub Message on Hub with lead data
- doc = {
- 'doctype': 'Lead',
- 'lead_name': frappe.db.get_single_value('Marketplace Settings', 'company'),
- 'email_id': frappe.db.get_single_value('Marketplace Settings', 'user')
- }
-
- args = frappe._dict(dict(
- doctype='Hub Message',
- reference_doctype='Lead',
- data=json.dumps(doc),
- user=contact.email_id
- ))
-
- connection = get_hub_connection()
- response = connection.insert('Hub Message', args)
-
- return response.ok
diff --git a/erpnext/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 6ce2a54..6144d9d 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
@@ -1,12 +1,14 @@
# 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
-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
@@ -50,14 +52,14 @@
GROUP BY p.loan_security
""".format(conditions=conditions), filters, as_list=1))
- for security, qty in iteritems(pledges):
+ for security, qty in pledges.items():
current_pledges.setdefault(security, qty)
current_pledges[security] -= unpledges.get(security, 0.0)
sorted_pledges = dict(sorted(current_pledges.items(), key=lambda item: item[1], reverse=True))
count = 0
- for security, qty in iteritems(sorted_pledges):
+ for security, qty in sorted_pledges.items():
values.append(qty * loan_security_details.get(security, {}).get('latest_price', 0))
labels.append(security)
count +=1
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index c9f23ca..5979992 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -334,7 +334,6 @@
},
{
"depends_on": "eval:doc.is_secured_loan",
- "fetch_from": "loan_application.maximum_loan_amount",
"fieldname": "maximum_loan_amount",
"fieldtype": "Currency",
"label": "Maximum Loan Amount",
@@ -360,7 +359,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-04-19 18:10:32.360818",
+ "modified": "2021-10-12 18:10:32.360818",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index ff7fbbd..84e0f03 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -1,16 +1,21 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 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
+from frappe.utils import add_months, flt, getdate, now_datetime, nowdate
+
+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):
@@ -129,16 +134,23 @@
frappe.throw(_("Loan amount is mandatory"))
def link_loan_security_pledge(self):
- if self.is_secured_loan:
- loan_security_pledge = frappe.db.get_value('Loan Security Pledge', {'loan_application': self.loan_application},
- 'name')
+ if self.is_secured_loan and self.loan_application:
+ maximum_loan_value = frappe.db.get_value('Loan Security Pledge',
+ {
+ 'loan_application': self.loan_application,
+ 'status': 'Requested'
+ },
+ 'sum(maximum_loan_value)'
+ )
- if loan_security_pledge:
- frappe.db.set_value('Loan Security Pledge', loan_security_pledge, {
- 'loan': self.name,
- 'status': 'Pledged',
- 'pledge_time': now_datetime()
- })
+ if maximum_loan_value:
+ frappe.db.sql("""
+ UPDATE `tabLoan Security Pledge`
+ SET loan = %s, pledge_time = %s, status = 'Pledged'
+ WHERE status = 'Requested' and loan_application = %s
+ """, (self.name, now_datetime(), self.loan_application))
+
+ self.db_set('maximum_loan_amount', maximum_loan_value)
def unlink_loan_security_pledge(self):
pledges = frappe.get_all('Loan Security Pledge', fields=['name'], filters={'loan': self.name})
@@ -308,7 +320,7 @@
@frappe.whitelist()
def unpledge_security(loan=None, loan_security_pledge=None, security_map=None, as_dict=0, save=0, submit=0, approve=0):
# if no security_map is passed it will be considered as full unpledge
- if security_map and isinstance(security_map, string_types):
+ if security_map and isinstance(security_map, str):
security_map = json.loads(security_map)
if loan:
@@ -361,7 +373,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 711a782..c8a9e64 100644
--- a/erpnext/loan_management/doctype/loan/loan_dashboard.py
+++ b/erpnext/loan_management/doctype/loan/loan_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'loan',
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 122d723..c0f058f 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -1,25 +1,40 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+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):
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index d8f3577..24d8d68 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -1,18 +1,26 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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
-from six import string_types
+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 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):
@@ -119,10 +127,11 @@
def create_loan(source_name, target_doc=None, submit=0):
def update_accounts(source_doc, target_doc, source_parent):
account_details = frappe.get_all("Loan Type",
- fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
- filters = {'name': source_doc.loan_type}
- )[0]
+ fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
+ filters = {'name': source_doc.loan_type})[0]
+ if source_doc.is_secured_loan:
+ target_doc.maximum_loan_amount = 0
target_doc.mode_of_payment = account_details.mode_of_payment
target_doc.payment_account = account_details.payment_account
@@ -180,7 +189,7 @@
#This is a sandbox method to get the proposed pledges
@frappe.whitelist()
def get_proposed_pledge(securities):
- if isinstance(securities, string_types):
+ if isinstance(securities, str):
securities = json.loads(securities)
proposed_pledges = {
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py b/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
index 3975adf..e8e2a2a 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'loan_application',
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..d367e92 100644
--- a/erpnext/loan_management/doctype/loan_application/test_loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/test_loan_application.py
@@ -1,12 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
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
+
+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 f113c10..93b4af9 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -1,17 +1,21 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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):
@@ -192,7 +196,7 @@
security_value = get_total_pledged_security_value(loan)
if loan_details.is_secured_loan and not on_current_security_price:
- security_value = flt(loan_details.maximum_loan_amount)
+ security_value = get_maximum_amount_as_per_pledged_security(loan)
if not security_value and not loan_details.is_secured_loan:
security_value = flt(loan_details.loan_amount)
@@ -203,3 +207,6 @@
disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
return disbursal_amount
+
+def get_maximum_amount_as_per_pledged_security(loan):
+ return flt(frappe.db.get_value('Loan Security Pledge', {'loan': loan}, 'sum(maximum_loan_value)'))
diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
index da56710..94ec84e 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
@@ -1,17 +1,43 @@
-# -*- coding: utf-8 -*-
# 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 d75213c..e945d49 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
@@ -1,15 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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):
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..46aaaad 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
@@ -1,16 +1,30 @@
-# -*- coding: utf-8 -*-
# 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 57aec2e..5922e4f 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -1,20 +1,25 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 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
+from frappe.utils import add_days, cint, date_diff, flt, get_datetime, getdate
+
+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 +112,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)
@@ -180,7 +186,7 @@
# interest_paid = self.amount_paid - self.principal_amount_paid - self.penalty_amount
if interest_paid > 0:
- for lia, amounts in iteritems(repayment_details.get('pending_accrual_entries', [])):
+ for lia, amounts in repayment_details.get('pending_accrual_entries', []).items():
if amounts['interest_amount'] + amounts['payable_principal_amount'] <= interest_paid:
interest_amount = amounts['interest_amount']
paid_principal = amounts['payable_principal_amount']
@@ -402,7 +408,7 @@
if due_date and not final_due_date:
final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
- if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'):
+ if against_loan_doc.status in ('Disbursed', 'Closed') or against_loan_doc.disbursed_amount >= against_loan_doc.loan_amount:
pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
- against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
else:
diff --git a/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py
index 73585a5..98e5a0a 100644
--- a/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..495b686 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..8087fc5 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security.py
+++ b/erpnext/loan_management/doctype/loan_security/loan_security.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 3eec566..964a1ae 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'loan_security',
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..7e702ed 100644
--- a/erpnext/loan_management/doctype/loan_security/test_loan_security.py
+++ b/erpnext/loan_management/doctype/loan_security/test_loan_security.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestLoanSecurity(unittest.TestCase):
pass
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..7d02645 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
@@ -1,14 +1,19 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..b9d05c2 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 9fc1fda..fca9dd6 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
@@ -1,13 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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):
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..aa533d9 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 cd7694b..20e451b 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
@@ -1,13 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 +73,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():
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..58bf974 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..af87259 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 17de8c1..2a9ceed 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,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'loan_security_type',
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..cf7a335 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 4f936dd..bff9d5c 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
@@ -1,15 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 get_datetime, flt, getdate
-import json
-from six import iteritems
-from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
+from frappe.utils import flt, get_datetime, getdate
+
class LoanSecurityUnpledge(Document):
def validate(self):
@@ -30,7 +27,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)
@@ -109,7 +108,7 @@
pledged_qty = 0
current_pledges = get_pledged_security_qty(self.loan)
- for security, qty in iteritems(current_pledges):
+ for security, qty in current_pledges.items():
pledged_qty += qty
if not pledged_qty:
@@ -142,7 +141,7 @@
GROUP BY p.loan_security
""", (loan)))
- for security, qty in iteritems(pledges):
+ for security, qty in pledges.items():
current_pledges.setdefault(security, qty)
current_pledges[security] -= unpledges.get(security, 0.0)
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..2f124e4 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 50ef930..592229c 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.py
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 LoanType(Document):
def validate(self):
self.validate_accounts()
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 95d97fd..245e102 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'loan_type',
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..e3b51e8 100644
--- a/erpnext/loan_management/doctype/loan_type/test_loan_type.py
+++ b/erpnext/loan_management/doctype/loan_type/test_loan_type.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 676df70..35be587 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
@@ -1,13 +1,15 @@
-# -*- 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, 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):
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..1494114 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
@@ -1,10 +1,9 @@
-# -*- 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 TestLoanWriteOff(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/pledge/pledge.py b/erpnext/loan_management/doctype/pledge/pledge.py
index 0457ad7..f02ed95 100644
--- a/erpnext/loan_management/doctype/pledge/pledge.py
+++ b/erpnext/loan_management/doctype/pledge/pledge.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..2d3fd32 100644
--- a/erpnext/loan_management/doctype/pledge/test_pledge.py
+++ b/erpnext/loan_management/doctype/pledge/test_pledge.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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 8c67c0a..4c34ccd 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
@@ -1,13 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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):
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 e104c66..932087c 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,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'process_loan_interest_accrual',
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..1fb8c2e 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..ba9fb0c 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
@@ -1,13 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 e67e4d4..1f5d843 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,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'process_loan_security_shortfall',
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..c743cf0 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..ac2b7d2 100644
--- a/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.py
+++ b/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..dc407e7 100644
--- a/erpnext/loan_management/doctype/repayment_schedule/repayment_schedule.py
+++ b/erpnext/loan_management/doctype/repayment_schedule/repayment_schedule.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..91267b8 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..6063b7b 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
@@ -1,11 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..4d99086 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..403749b 100644
--- a/erpnext/loan_management/doctype/unpledge/unpledge.py
+++ b/erpnext/loan_management/doctype/unpledge/unpledge.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 Unpledge(Document):
pass
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 f2cbbb4..512b47f 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
@@ -1,12 +1,13 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 six import iteritems
+from frappe.utils import flt
+
+import erpnext
+
def execute(filters=None):
columns = get_columns(filters)
@@ -42,7 +43,7 @@
currency = erpnext.get_company_currency(filters.get('company'))
- for key, qty in iteritems(pledge_values):
+ for key, qty in pledge_values.items():
if qty:
row = {}
current_value = flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
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 a505e72..7c51267 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
@@ -1,13 +1,15 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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):
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 6591077..68fd3d8 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
@@ -1,10 +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 = get_columns()
data = get_data(filters)
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 34bbe5a..7dbb966 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
@@ -1,13 +1,16 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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)
@@ -39,7 +42,7 @@
current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details)
currency = erpnext.get_company_currency(filters.get('company'))
- for security, value in iteritems(current_pledges):
+ for security, value in current_pledges.items():
if value.get('qty'):
row = {}
current_value = flt(value.get('qty', 0) * loan_security_details.get(security, {}).get('latest_price', 0))
@@ -66,7 +69,7 @@
total_portfolio_value = 0
security_wise_map = {}
- for key, qty in iteritems(pledge_values):
+ for key, qty in pledge_values.items():
security_wise_map.setdefault(key[1], {
'qty': 0.0,
'applicant_count': 0.0
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..b7e7168 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
@@ -1,10 +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 = get_columns(filters)
data = get_data(filters)
diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json
index ca528ec..7deee0d 100644
--- a/erpnext/loan_management/workspace/loan_management/loan_management.json
+++ b/erpnext/loan_management/workspace/loan_management/loan_management.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Processes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Disbursement and Repayment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Security\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-12 16:35:55.299820",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "loan",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Loans",
"links": [
{
@@ -245,15 +238,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:18:13.350904",
+ "modified": "2021-08-05 12:18:13.350905",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loans",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
index d1a8c8d..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);
},
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 9728903..2ffae1a 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -1,23 +1,23 @@
# 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 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,7 +46,7 @@
"Yearly": 365
}
for item in self.items:
- if item.periodicity and item.start_date:
+ if item.periodicity and item.periodicity != "Random" and item.start_date:
if not item.end_date:
if item.no_of_visits:
item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
@@ -198,12 +198,16 @@
if chk:
throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
+ def validate_no_of_visits(self):
+ return len(self.schedules) != sum(d.no_of_visits for d in self.items)
+
def validate(self):
self.validate_end_date_visits()
self.validate_maintenance_detail()
self.validate_dates_with_periodicity()
self.validate_sales_order()
- self.generate_schedule()
+ if not self.schedules or self.validate_no_of_visits():
+ self.generate_schedule()
def on_update(self):
frappe.db.set(self, 'status', 'Draft')
@@ -319,10 +323,14 @@
target.maintenance_schedule = source.name
target.maintenance_schedule_detail = s_id
- def update_sales(source, target, parent):
+ def update_sales_and_serial(source, target, parent):
sales_person = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'sales_person')
target.service_person = sales_person
- target.serial_no = ''
+ serial_nos = get_serial_nos(target.serial_no)
+ if len(serial_nos) == 1:
+ target.serial_no = serial_nos[0]
+ else:
+ target.serial_no = ''
doclist = get_mapped_doc("Maintenance Schedule", source_name, {
"Maintenance Schedule": {
@@ -338,7 +346,7 @@
"Maintenance Schedule Item": {
"doctype": "Maintenance Visit Purpose",
"condition": lambda doc: doc.item_name == item_name,
- "postprocess": update_sales
+ "postprocess": update_sales_and_serial
}
}, target_doc)
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 c733dd0..37ea3fd 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
@@ -1,12 +1,14 @@
-# -*- coding: utf-8 -*-
# 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')
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..cb20066 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.py
@@ -1,10 +1,9 @@
-# -*- 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.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..b6ce0a5 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.py
@@ -1,10 +1,9 @@
-# -*- 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.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..6f6ca61 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,11 +43,11 @@
}
});
}
-
if (!frm.doc.status) {
frm.set_value({ status: 'Draft' });
}
if (frm.doc.__islocal) {
+ frm.clear_table("purposes");
frm.set_value({ mntc_date: frappe.datetime.get_today() });
}
},
@@ -73,12 +73,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 +108,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 d63c700..5a87b16 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
@@ -1,13 +1,14 @@
# 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 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)
diff --git a/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
index 2bea8d1..6a9e70a 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
@@ -1,12 +1,41 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+from frappe.utils.data import today
# test_records = frappe.get_test_records('Maintenance Visit')
class TestMaintenanceVisit(unittest.TestCase):
pass
+
+def make_maintenance_visit():
+ mv = frappe.new_doc("Maintenance Visit")
+ mv.company = "_Test Company"
+ mv.customer = "_Test Customer"
+ mv.mntc_date = today()
+ mv.completion_status = "Partially Completed"
+
+ sales_person = make_sales_person("Dwight Schrute")
+
+ mv.append("purposes", {
+ "item_code": "_Test Item",
+ "sales_person": "Sales Team",
+ "description": "Test Item",
+ "work_done": "Test Work Done",
+ "service_person": sales_person.name
+ })
+ mv.insert(ignore_permissions=True)
+
+ return mv
+
+def make_sales_person(name):
+ sales_person = frappe.get_doc({
+ 'doctype': "Sales Person",
+ 'sales_person_name': name
+ })
+ sales_person.insert(ignore_if_duplicate = True)
+
+ return sales_person
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..50d9a4e 100644
--- a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py
+++ b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py
@@ -1,10 +1,9 @@
-# -*- 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.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/__init__.py b/erpnext/manufacturing/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/__init__.py
+++ b/erpnext/manufacturing/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index 1aedb1e..5340c51 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -1,13 +1,13 @@
-# -*- 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.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
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
index d9aa0ca..c6745c8 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'blanket_order',
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 9a0a72f..d5db3fc 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -1,14 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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()
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..ebce209 100644
--- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
+++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
@@ -1,9 +1,9 @@
-# -*- 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 BlanketOrderItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/bom/__init__.py b/erpnext/manufacturing/doctype/bom/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/bom/__init__.py
+++ b/erpnext/manufacturing/doctype/bom/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index e72c8eb..6d35d65 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();
+ }
});
}
@@ -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");
@@ -650,8 +680,57 @@
erpnext.bom.calculate_total(frm.doc);
});
-frappe.ui.form.on("BOM", "with_operations", function(frm) {
- if(!cint(frm.doc.with_operations)) {
- 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.json b/erpnext/manufacturing/doctype/bom/bom.json
index 7e53918..218ac64 100644
--- a/erpnext/manufacturing/doctype/bom/bom.json
+++ b/erpnext/manufacturing/doctype/bom/bom.json
@@ -237,6 +237,7 @@
"options": "Price List"
},
{
+ "depends_on": "with_operations",
"fieldname": "operations_section",
"fieldtype": "Section Break",
"hide_border": 1,
@@ -436,7 +437,7 @@
"description": "Item Image (if not slideshow)",
"fieldname": "website_image",
"fieldtype": "Attach Image",
- "label": "Image"
+ "label": "Website Image"
},
{
"allow_on_submit": 1,
@@ -539,7 +540,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
- "modified": "2021-05-16 12:25:09.081968",
+ "modified": "2021-11-18 13:04:16.271975",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index eb1dfc8..2cd8f8c 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"
@@ -156,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') }]
@@ -230,7 +230,7 @@
}
ret = self.get_bom_material_detail(args)
for key, value in ret.items():
- if not item.get(key):
+ if item.get(key) is None:
item.set(key, value)
@frappe.whitelist()
@@ -307,6 +307,9 @@
existing_bom_cost = self.total_cost
for d in self.get("items"):
+ if not d.item_code:
+ continue
+
rate = self.get_rm_rate({
"company": self.company,
"item_code": d.item_code,
@@ -446,25 +449,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):
@@ -506,27 +513,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()
@@ -583,7 +602,7 @@
for d in self.get('items'):
if d.bom_no:
self.get_child_exploded_items(d.bom_no, d.stock_qty)
- else:
+ elif d.item_code:
self.add_to_cur_exploded_items(frappe._dict({
'item_code' : d.item_code,
'item_name' : d.item_name,
@@ -705,6 +724,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)
@@ -822,8 +867,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..0699f74 100644
--- a/erpnext/manufacturing/doctype/bom/bom_dashboard.py
+++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py
@@ -1,6 +1,6 @@
-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 e614a7e..eb4135e 100644
--- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html
+++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
@@ -16,26 +16,15 @@
</div>
<hr style="margin: 15px -15px;">
<p>
- {% if data.value %}
- <a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="#Form/BOM/{{ data.value }}">
+ {% if data.value && data.value != "BOM" %}
+ <a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="/app/bom/{{ data.value }}">
{{ __("Open BOM {0}", [data.value.bold()]) }}</a>
{% endif %}
{% if data.item_code %}
- <a class="btn btn-default btn-xs" href="#Form/Item/{{ data.item_code }}">
+ <a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="/app/item/{{ data.item_code }}">
{{ __("Open Item {0}", [data.item_code.bold()]) }}</a>
{% endif %}
</p>
</div>
</div>
- <hr style="margin: 15px -15px;">
- <p>
- {% if data.value %}
- <a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="/app/Form/BOM/{{ data.value }}">
- {{ __("Open BOM {0}", [data.value.bold()]) }}</a>
- {% endif %}
- {% if data.item_code %}
- <a class="btn btn-default btn-xs" href="/app/Form/Item/{{ data.item_code }}">
- {{ __("Open Item {0}", [data.item_code.bold()]) }}</a>
- {% endif %}
- </p>
</div>
diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js
index 6e2599e..fb99add 100644
--- a/erpnext/manufacturing/doctype/bom/bom_tree.js
+++ b/erpnext/manufacturing/doctype/bom/bom_tree.js
@@ -66,6 +66,7 @@
var bom = frappe.model.get_doc("BOM", node.data.value);
node.data.image = escape(bom.image) || "";
node.data.description = bom.description || "";
+ node.data.item_code = bom.item || "";
});
}
},
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/__init__.py b/erpnext/manufacturing/doctype/bom_explosion_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/bom_explosion_item/__init__.py
+++ b/erpnext/manufacturing/doctype/bom_explosion_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 39ccbdd..cbcba34 100644
--- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py
+++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class BOMExplosionItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/bom_item/__init__.py b/erpnext/manufacturing/doctype/bom_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/bom_item/__init__.py
+++ b/erpnext/manufacturing/doctype/bom_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.py b/erpnext/manufacturing/doctype/bom_item/bom_item.py
index 220c73e..28a4b20 100644
--- a/erpnext/manufacturing/doctype/bom_item/bom_item.py
+++ b/erpnext/manufacturing/doctype/bom_item/bom_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class BOMItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/bom_operation/__init__.py b/erpnext/manufacturing/doctype/bom_operation/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/bom_operation/__init__.py
+++ b/erpnext/manufacturing/doctype/bom_operation/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 e3501eb..0ddc280 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.py
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class BOMOperation(Document):
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..f400303b 100644
--- a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.py
+++ b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class BOMScrapItem(Document):
pass
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..0e3955f 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
@@ -1,15 +1,17 @@
-# -*- 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, 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 erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order
+
class BOMUpdateTool(Document):
def replace_bom(self):
@@ -76,7 +78,7 @@
@frappe.whitelist()
def enqueue_replace_bom(args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=40000)
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..526c243 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
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
-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..33256a3 100644
--- a/erpnext/manufacturing/doctype/bom_website_item/bom_website_item.py
+++ b/erpnext/manufacturing/doctype/bom_website_item/bom_website_item.py
@@ -1,10 +1,9 @@
-# -*- 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.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..f8e2792 100644
--- a/erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.py
+++ b/erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.py
@@ -1,10 +1,9 @@
-# -*- 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.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..4602816 100644
--- a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py
+++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
-from frappe.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..2f99a5c 100644
--- a/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py
+++ b/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py
@@ -1,10 +1,9 @@
-# -*- 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 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 91eb4a0..453ad50 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -26,15 +26,28 @@
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.__onload.work_order_closed) {
+ frm.disable_save();
+ return;
+ }
+
+ 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");
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index 046e2fd..6528199 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,29 @@
"fieldtype": "Link",
"label": "Batch No",
"options": "Batch"
+ },
+ {
+ "collapsible": 1,
+ "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-11-12 10:15:03.572401",
"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 3efbe88..8d00019 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -1,18 +1,30 @@
-# -*- 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,6 +33,11 @@
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)
+ self.set_onload("work_order_closed", self.is_work_order_closed())
+
def validate(self):
self.validate_time_logs()
self.set_status()
@@ -28,6 +45,7 @@
self.validate_sequence_id()
self.set_sub_operations()
self.update_sub_operation_status()
+ self.validate_work_order()
def set_sub_operations(self):
if self.operation:
@@ -75,7 +93,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 +451,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 +460,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,11 +504,11 @@
self.status = 'Work In Progress'
if (self.docstatus == 1 and
- (self.for_quantity == self.transferred_qty or not self.items)):
+ (self.for_quantity <= self.total_completed_qty or not self.items)):
self.status = 'Completed'
if self.status != 'Completed':
- if self.for_quantity == self.transferred_qty:
+ if self.for_quantity <= self.transferred_qty:
self.status = 'Material Transferred'
if update_status:
@@ -528,6 +548,18 @@
frappe.throw(_("{0}, complete the operation {1} before the operation {2}.")
.format(message, bold(row.operation), bold(self.operation)), OperationSequenceError)
+ def validate_work_order(self):
+ if self.is_work_order_closed():
+ frappe.throw(_("You can't make any changes to Job Card since Work Order is closed."))
+
+ def is_work_order_closed(self):
+ if self.work_order:
+ status = frappe.get_value('Work Order', self.work_order)
+
+ if status == "Closed":
+ return True
+
+ return False
@frappe.whitelist()
def make_time_log(args):
@@ -584,7 +616,8 @@
"doctype": "Material Request Item",
"field_map": {
"required_qty": "qty",
- "uom": "stock_uom"
+ "uom": "stock_uom",
+ "name": "job_card_item"
},
"postprocess": update_item,
}
@@ -594,15 +627,24 @@
@frappe.whitelist()
def make_stock_entry(source_name, target_doc=None):
- def update_item(obj, target, source_parent):
+ def update_item(source, target, source_parent):
target.t_warehouse = source_parent.wip_warehouse
+
if not target.conversion_factor:
target.conversion_factor = 1
+ pending_rm_qty = flt(source.required_qty) - flt(source.transferred_qty)
+ if pending_rm_qty > 0:
+ target.qty = pending_rm_qty
+
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 = flt(source.get('for_quantity', 0)) - flt(source.get('transferred_qty', 0))
+ target.fg_completed_qty = pending_fg_qty if pending_fg_qty > 0 else 0
+
target.set_transfer_qty()
target.calculate_rate_and_amount()
target.set_missing_values()
@@ -652,7 +694,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 +704,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))
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
index c2aa2bd..2c48872 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
@@ -1,13 +1,20 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'job_card',
+ 'non_standard_fieldnames': {
+ 'Quality Inspection': 'reference_name'
+ },
'transactions': [
{
'label': _('Transactions'),
'items': ['Material Request', 'Stock Entry']
+ },
+ {
+ 'label': _('Reference'),
+ 'items': ['Quality Inspection']
}
]
}
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 8fa0b27..9b4fc8b 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -1,75 +1,332 @@
-# -*- 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):
+ make_bom_for_jc_tests()
+
+ transfer_material_against, source_warehouse = None, None
+
+ tests_that_skip_setup = (
+ "test_job_card_material_transfer_correctness",
+ )
+ tests_that_transfer_against_jc = (
+ "test_job_card_multiple_materials_transfer",
+ "test_job_card_excess_material_transfer",
+ "test_job_card_partial_material_transfer"
+ )
+
+ if self._testMethodName in tests_that_skip_setup:
+ return
+
+ if self._testMethodName in tests_that_transfer_against_jc:
+ transfer_material_against = "Job Card"
+ source_warehouse = "Stores - _TC"
+
+ self.work_order = make_wo_order_test_record(
+ item="_Test FG Item 2",
+ qty=2,
+ transfer_material_against=transfer_material_against,
+ source_warehouse=source_warehouse
+ )
+
+ def 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)
-
- doc = frappe.get_doc("Job Card", job_card.name)
- doc.operation_id = "Test Data"
- self.assertRaises(OperationMismatchError, doc.save)
-
- for d in job_cards:
- frappe.delete_doc("Job Card", d.name)
+ 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'])
+ job_cards = frappe.get_all('Job Card',
+ filters = {'work_order': self.work_order.name},
+ fields = ["operation_id", "workstation", "name", "for_quantity"])
- if data:
- bom, bom_item = data
+ job_card = job_cards[0]
- work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
+ if job_card:
+ workstation = frappe.db.get_value("Workstation",
+ {"name": ("not in", [job_card.workstation])}, "name")
- job_cards = frappe.get_all('Job Card',
- filters = {'work_order': work_order.name},
- fields = ["operation_id", "workstation", "name", "for_quantity"])
+ if not workstation or job_card.workstation == workstation:
+ workstation = make_workstation(workstation_name=random_string(5)).name
- job_card = job_cards[0]
+ 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()
- if job_card:
- workstation = frappe.db.get_value("Workstation",
- {"name": ("not in", [job_card.workstation])}, "name")
+ completed_qty = frappe.db.get_value("Work Order Operation", job_card.operation_id, "completed_qty")
+ self.assertEqual(completed_qty, job_card.for_quantity)
- 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()
+ doc.cancel()
for d in job_cards:
frappe.delete_doc("Job Card", d.name)
+
+ def test_job_card_overlap(self):
+ wo2 = make_wo_order_test_record(item="_Test FG Item 2", qty=2)
+
+ jc1_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name})
+ jc2_name = frappe.db.get_value("Job Card", {'work_order': wo2.name})
+
+ jc1 = frappe.get_doc("Job Card", jc1_name)
+ jc2 = frappe.get_doc("Job Card", jc2_name)
+
+ employee = "_T-Employee-00001" # from test records
+
+ jc1.append("time_logs", {
+ "from_time": "2021-01-01 00:00:00",
+ "to_time": "2021-01-01 08:00:00",
+ "completed_qty": 1,
+ "employee": employee,
+ })
+ jc1.save()
+
+ # add a new entry in same time slice
+ jc2.append("time_logs", {
+ "from_time": "2021-01-01 00:01:00",
+ "to_time": "2021-01-01 06:00:00",
+ "completed_qty": 1,
+ "employee": employee,
+ })
+ self.assertRaises(OverlapError, jc2.save)
+
+ def test_job_card_multiple_materials_transfer(self):
+ "Test transferring RMs separately against Job Card with multiple RMs."
+ make_stock_entry(
+ item_code="_Test Item",
+ target="Stores - _TC",
+ qty=10,
+ basic_rate=100
+ )
+ make_stock_entry(
+ item_code="_Test Item Home Desktop Manufactured",
+ target="Stores - _TC",
+ qty=6,
+ basic_rate=100
+ )
+
+ job_card_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name})
+ job_card = frappe.get_doc("Job Card", job_card_name)
+
+ transfer_entry_1 = make_stock_entry_from_jc(job_card_name)
+ del transfer_entry_1.items[1] # transfer only 1 of 2 RMs
+ transfer_entry_1.insert()
+ transfer_entry_1.submit()
+
+ job_card.reload()
+
+ 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")
+
+ def test_job_card_partial_material_transfer(self):
+ "Test partial material transfer 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)
+
+ # partially transfer
+ transfer_entry = make_stock_entry_from_jc(job_card_name)
+ transfer_entry.fg_completed_qty = 1
+ transfer_entry.get_items()
+ transfer_entry.insert()
+ transfer_entry.submit()
+
+ job_card.reload()
+ self.assertEqual(job_card.transferred_qty, 1)
+ self.assertEqual(transfer_entry.items[0].qty, 5)
+ self.assertEqual(transfer_entry.items[1].qty, 3)
+
+ # transfer remaining
+ transfer_entry_2 = make_stock_entry_from_jc(job_card_name)
+
+ self.assertEqual(transfer_entry_2.fg_completed_qty, 1)
+ self.assertEqual(transfer_entry_2.items[0].qty, 5)
+ self.assertEqual(transfer_entry_2.items[1].qty, 3)
+
+ transfer_entry_2.insert()
+ transfer_entry_2.submit()
+
+ job_card.reload()
+ self.assertEqual(job_card.transferred_qty, 2)
+
+ def test_job_card_material_transfer_correctness(self):
+ """
+ 1. Test if only current Job Card Items are pulled in a Stock Entry against a Job Card
+ 2. Test impact of changing 'For Qty' in such a Stock Entry
+ """
+ create_bom_with_multiple_operations()
+ work_order = make_wo_with_transfer_against_jc()
+
+ job_card_name = frappe.db.get_value(
+ "Job Card",
+ {"work_order": work_order.name,"operation": "Test Operation A"}
+ )
+ job_card = frappe.get_doc("Job Card", job_card_name)
+
+ self.assertEqual(len(job_card.items), 1)
+ self.assertEqual(job_card.items[0].item_code, "_Test Item")
+
+ # check if right items are mapped in transfer entry
+ transfer_entry = make_stock_entry_from_jc(job_card_name)
+ transfer_entry.insert()
+
+ self.assertEqual(len(transfer_entry.items), 1)
+ self.assertEqual(transfer_entry.items[0].item_code, "_Test Item")
+ self.assertEqual(transfer_entry.items[0].qty, 4)
+
+ # change 'For Qty' and check impact on items table
+ # no.of items should be the same with qty change
+ transfer_entry.fg_completed_qty = 2
+ transfer_entry.get_items()
+
+ self.assertEqual(len(transfer_entry.items), 1)
+ self.assertEqual(transfer_entry.items[0].item_code, "_Test Item")
+ self.assertEqual(transfer_entry.items[0].qty, 2)
+
+ # rollback via tearDown method
+
+def create_bom_with_multiple_operations():
+ "Create a BOM with multiple operations and Material Transfer against Job Card"
+ from erpnext.manufacturing.doctype.operation.test_operation import make_operation
+
+ test_record = frappe.get_test_records("BOM")[2]
+ bom_doc = frappe.get_doc(test_record)
+
+ row = {
+ "operation": "Test Operation A",
+ "workstation": "_Test Workstation A",
+ "hour_rate_rent": 300,
+ "time_in_mins": 60
+ }
+ make_workstation(row)
+ make_operation(row)
+
+ bom_doc.append("operations", {
+ "operation": "Test Operation A",
+ "description": "Test Operation A",
+ "workstation": "_Test Workstation A",
+ "hour_rate": 300,
+ "time_in_mins": 60,
+ "operating_cost": 100
+ })
+
+ bom_doc.transfer_material_against = "Job Card"
+ bom_doc.save()
+ bom_doc.submit()
+
+ return bom_doc
+
+def make_wo_with_transfer_against_jc():
+ "Create a WO with multiple operations and Material Transfer against Job Card"
+
+ work_order = make_wo_order_test_record(
+ item="_Test FG Item 2",
+ qty=4,
+ transfer_material_against="Job Card",
+ source_warehouse="Stores - _TC",
+ do_not_submit=True
+ )
+ work_order.required_items[0].operation = "Test Operation A"
+ work_order.required_items[1].operation = "_Test Operation 1"
+ work_order.submit()
+
+ return work_order
+
+def make_bom_for_jc_tests():
+ test_records = frappe.get_test_records('BOM')
+ bom = frappe.copy_doc(test_records[2])
+ bom.set_rate_of_sub_assembly_item_based_on_bom = 0
+ bom.rm_cost_as_per = "Valuation Rate"
+ bom.items[0].uom = "_Test UOM 1"
+ bom.items[0].conversion_factor = 5
+ bom.insert()
\ 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..51a7b41 100644
--- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.py
+++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.py
@@ -1,9 +1,9 @@
-# -*- 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 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..de44071 100644
--- a/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.py
+++ b/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..2b3ead3 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
@@ -1,9 +1,9 @@
-# -*- 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 JobCardTimeLog(Document):
pass
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 149fe3e..c919e8b 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
@@ -1,11 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
+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
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..1b2f18f 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.py
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..3d5a7ce 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
@@ -1,9 +1,9 @@
-# -*- 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 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..0654c1e 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestMaterialRequestPlanItem(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/operation/operation.js b/erpnext/manufacturing/doctype/operation/operation.js
index 2936e33..ea73fd6 100644
--- a/erpnext/manufacturing/doctype/operation/operation.js
+++ b/erpnext/manufacturing/doctype/operation/operation.js
@@ -12,3 +12,21 @@
});
}
});
+
+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..41726f3 100644
--- a/erpnext/manufacturing/doctype/operation/operation.py
+++ b/erpnext/manufacturing/doctype/operation/operation.py
@@ -1,12 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
import frappe
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..4fbcf49 100644
--- a/erpnext/manufacturing/doctype/operation/operation_dashboard.py
+++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py
@@ -1,6 +1,6 @@
-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 8e7e723..e511084 100644
--- a/erpnext/manufacturing/doctype/operation/test_operation.py
+++ b/erpnext/manufacturing/doctype/operation/test_operation.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
test_records = frappe.get_test_records('Operation')
@@ -17,15 +17,13 @@
args = frappe._dict(args)
- try:
+ if not frappe.db.exists("Operation", args.operation):
doc = frappe.get_doc({
"doctype": "Operation",
"name": args.operation,
"workstation": args.workstation
})
-
doc.insert()
-
return doc
- except frappe.DuplicateEntryError:
- return frappe.get_doc("Operation", args.operation)
+
+ 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..0babf87 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -105,7 +105,7 @@
}
frm.trigger("material_requirement");
- const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
+ const projected_qty_formula = ` <table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
<tr><td style="padding-left:25px">
<div>
<h3 style="text-decoration: underline;">
@@ -238,10 +238,18 @@
method: "get_items",
freeze: true,
doc: frm.doc,
+ callback: function() {
+ frm.refresh_field("po_items");
+ if (frm.doc.sub_assembly_items.length > 0) {
+ frm.trigger("get_sub_assembly_items");
+ }
+ }
});
},
get_sub_assembly_items: function(frm) {
+ frm.dirty();
+
frappe.call({
method: "get_sub_assembly_items",
freeze: true,
@@ -254,7 +262,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 +273,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 +359,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 +442,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 +468,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 b4c6635..7cec7f5 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -1,20 +1,31 @@
-# -*- 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, json, copy
-from frappe import msgprint, _
-from six import iteritems
+import copy
+import json
+
+import frappe
+from frappe import _, msgprint
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 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 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()
@@ -213,7 +224,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,
@@ -224,6 +234,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
@@ -297,7 +308,7 @@
if self.total_produced_qty > 0:
self.status = "In Process"
- if self.total_produced_qty >= self.total_planned_qty:
+ if self.check_have_work_orders_completed():
self.status = "Completed"
if self.status != 'Completed':
@@ -331,7 +342,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,
@@ -346,8 +357,7 @@
"production_plan" : self.name,
"production_plan_item" : d.name,
"product_bundle_item" : d.product_bundle_item,
- "planned_start_date" : d.planned_start_date,
- "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({
@@ -411,7 +421,7 @@
po = frappe.new_doc('Purchase Order')
po.supplier = supplier
po.schedule_date = getdate(po_list[0].schedule_date) if po_list[0].schedule_date else nowdate()
- po.is_subcontracted_item = 'Yes'
+ po.is_subcontracted = 'Yes'
for row in po_list:
args = {
'item_code': row.production_item,
@@ -444,7 +454,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({
@@ -454,10 +465,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")
@@ -544,8 +559,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)
@@ -559,6 +572,15 @@
self.append("sub_assembly_items", data)
+ def check_have_work_orders_completed(self):
+ wo_status = frappe.db.get_list(
+ "Work Order",
+ filters={"production_plan": self.name},
+ fields="status",
+ pluck="status"
+ )
+ return all(s == "Completed" for s in wo_status)
+
@frappe.whitelist()
def download_raw_materials(doc, warehouses=None):
if isinstance(doc, str):
@@ -569,7 +591,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')])
@@ -605,9 +630,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("""
@@ -642,6 +674,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:
@@ -669,10 +704,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)
@@ -704,43 +740,42 @@
def get_sales_orders(self):
so_filter = item_filter = ""
bom_item = "bom.item = so_item.item_code"
- 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"
+
+ 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')
+ }
+
+ 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)s"
+ item_filter += " and so_item.item_code = %(item_code)s"
- open_so = frappe.db.sql("""
+ 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 {2}
+ 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, bom_item), {
- "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()
@@ -769,6 +804,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 = []
@@ -841,10 +882,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(
{
@@ -866,7 +905,7 @@
sales_order = doc.get("sales_order")
- for item_code, details in iteritems(item_details):
+ for item_code, details in item_details.items():
so_item_details.setdefault(sales_order, frappe._dict())
if item_code in so_item_details.get(sales_order, {}):
so_item_details[sales_order][item_code]['qty'] = so_item_details[sales_order][item_code].get("qty", 0) + flt(details.qty)
@@ -874,7 +913,7 @@
so_item_details[sales_order][item_code] = details
mr_items = []
- for sales_order, item_code in iteritems(so_item_details):
+ for sales_order, item_code in so_item_details.items():
item_dict = so_item_details[sales_order]
for details in item_dict.values():
bin_dict = get_bin_details(details, doc.company, warehouse)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
index 52a56af..e13f042 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'production_plan',
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 a5b9ff8..a2980a7 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -1,17 +1,23 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from frappe.utils import nowdate, now_datetime, flt, add_to_date
-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
-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 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.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):
@@ -288,6 +294,7 @@
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 = {
@@ -300,20 +307,20 @@
}
item.update(variant_settings)
item.save()
- parent_bom = make_bom(item = 'PIV', raw_materials = ['PIV'])
+ 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 = ['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 = [variant.item_code])
+ variant_bom = make_bom(item = variant.item_code, raw_materials = [rm_item.item_code])
else:
variant = frappe.get_doc('Item', 'PIV-RED')
if not frappe.db.exists('BOM', {"item": 'PIV-RED'}):
- variant_bom = make_bom(item = variant.item_code, raw_materials = [variant.item_code])
+ 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)
@@ -395,6 +402,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/__init__.py b/erpnext/manufacturing/doctype/production_plan_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/production_plan_item/__init__.py
+++ b/erpnext/manufacturing/doctype/production_plan_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 37cf5a4..cc79ac3 100644
--- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py
+++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class ProductionPlanItem(Document):
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..81d2eca 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
@@ -1,10 +1,10 @@
-# -*- 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 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..83b1789 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
@@ -1,10 +1,9 @@
-# -*- 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.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..a66ff44 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProductionPlanMaterialRequestWarehouse(Document):
pass
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py
index ecab5fb..4394c14 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestProductionPlanMaterialRequestWarehouse(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/production_plan_sales_order/__init__.py b/erpnext/manufacturing/doctype/production_plan_sales_order/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/production_plan_sales_order/__init__.py
+++ b/erpnext/manufacturing/doctype/production_plan_sales_order/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 99c7273..3f38529 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class ProductionPlanSalesOrder(Document):
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..069667a 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..1c76634 100644
--- a/erpnext/manufacturing/doctype/routing/routing.py
+++ b/erpnext/manufacturing/doctype/routing/routing.py
@@ -1,12 +1,12 @@
-# -*- 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.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 50a3fe6..d051e38 100644
--- a/erpnext/manufacturing/doctype/routing/routing_dashboard.py
+++ b/erpnext/manufacturing/doctype/routing/routing_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'routing',
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..68d9dec 100644
--- a/erpnext/manufacturing/doctype/routing/test_routing.py
+++ b/erpnext/manufacturing/doctype/routing/test_routing.py
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-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 +92,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..c86058e 100644
--- a/erpnext/manufacturing/doctype/sub_operation/sub_operation.py
+++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..189fdae 100644
--- a/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
+++ b/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
@@ -1,10 +1,9 @@
-# -*- 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 TestSubOperation(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/work_order/__init__.py b/erpnext/manufacturing/doctype/work_order/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/work_order/__init__.py
+++ b/erpnext/manufacturing/doctype/work_order/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index bf1ccb7..f590d68 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -1,22 +1,30 @@
-# 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 erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
-class TestWorkOrder(unittest.TestCase):
+import frappe
+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,
+ close_work_order,
+ 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
+from erpnext.tests.utils import ERPNextTestCase, timeout
+
+
+class TestWorkOrder(ERPNextTestCase):
def setUp(self):
self.warehouse = '_Test Warehouse 2 - _TC'
self.item = '_Test Item'
@@ -369,6 +377,7 @@
self.assertEqual(len(ste.additional_costs), 1)
self.assertEqual(ste.total_additional_costs, 1000)
+ @timeout(seconds=60)
def test_job_card(self):
stock_entries = []
bom = frappe.get_doc('BOM', {
@@ -675,13 +684,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 +704,168 @@
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
+ )
+
+ @timeout(seconds=60)
+ def test_job_card_scrap_item(self):
+ items = ['Test FG Item for Scrap Item Test', 'Test RM Item 1 for Scrap Item Test',
+ 'Test RM Item 2 for Scrap Item Test']
+
+ 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 test_close_work_order(self):
+ items = ['Test FG Item for Closed WO', 'Test RM Item 1 for Closed WO',
+ 'Test RM Item 2 for Closed WO']
+
+ 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 Closed WO'
+ raw_materials = ['Test RM Item 1 for Closed WO', 'Test RM Item 2 for Closed WO']
+ if not frappe.db.get_value('BOM', {'item': item}):
+ bom = make_bom(item=item, source_warehouse='Stores - TCP1', raw_materials=raw_materials, do_not_save=True)
+ bom.with_operations = 1
+ bom.append('operations', {
+ 'operation': '_Test Operation 1',
+ 'workstation': '_Test Workstation 1',
+ 'hour_rate': 20,
+ 'time_in_mins': 60
+ })
+
+ bom.submit()
+
+ wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1)
+ job_cards = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name')
+
+ if len(job_cards) == len(bom.operations):
+ for jc in job_cards:
+ job_card_doc = frappe.get_doc('Job Card', jc)
+ job_card_doc.append('time_logs', {
+ 'from_time': now(),
+ 'time_in_mins': 60,
+ 'completed_qty': job_card_doc.for_quantity
+ })
+
+ job_card_doc.submit()
+
+ close_work_order(wo_order, "Closed")
+ self.assertEqual(wo_order.get('status'), "Closed")
+
+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 +908,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..5ffbb03 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -135,24 +135,26 @@
frm.set_intro(__("Submit this Work Order for further processing."));
}
- if (frm.doc.docstatus===1) {
- frm.trigger('show_progress_for_items');
- frm.trigger('show_progress_for_operations');
- }
+ if (frm.doc.status != "Closed") {
+ if (frm.doc.docstatus===1) {
+ frm.trigger('show_progress_for_items');
+ frm.trigger('show_progress_for_operations');
+ }
- if (frm.doc.docstatus === 1
- && frm.doc.operations && frm.doc.operations.length) {
+ if (frm.doc.docstatus === 1
+ && frm.doc.operations && frm.doc.operations.length) {
- const not_completed = frm.doc.operations.filter(d => {
- if(d.status != 'Completed') {
- return true;
+ const not_completed = frm.doc.operations.filter(d => {
+ if (d.status != 'Completed') {
+ return true;
+ }
+ });
+
+ if (not_completed && not_completed.length) {
+ frm.add_custom_button(__('Create Job Card'), () => {
+ frm.trigger("make_job_card");
+ }).addClass('btn-primary');
}
- });
-
- if(not_completed && not_completed.length) {
- frm.add_custom_button(__('Create Job Card'), () => {
- frm.trigger("make_job_card");
- }).addClass('btn-primary');
}
}
@@ -440,7 +442,7 @@
additional_operating_cost: function(frm) {
erpnext.work_order.calculate_cost(frm.doc);
erpnext.work_order.calculate_total_cost(frm);
- }
+ },
});
frappe.ui.form.on("Work Order Item", {
@@ -517,14 +519,22 @@
erpnext.work_order = {
set_custom_buttons: function(frm) {
var doc = frm.doc;
- if (doc.docstatus === 1) {
+ if (doc.docstatus === 1 && doc.status != "Closed") {
+ frm.add_custom_button(__('Close'), function() {
+ frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."),
+ () => {
+ erpnext.work_order.change_work_order_status(frm, "Closed");
+ }
+ );
+ }, __("Status"));
+
if (doc.status != 'Stopped' && doc.status != 'Completed') {
frm.add_custom_button(__('Stop'), function() {
- erpnext.work_order.stop_work_order(frm, "Stopped");
+ erpnext.work_order.change_work_order_status(frm, "Stopped");
}, __("Status"));
} else if (doc.status == 'Stopped') {
frm.add_custom_button(__('Re-open'), function() {
- erpnext.work_order.stop_work_order(frm, "Resumed");
+ erpnext.work_order.change_work_order_status(frm, "Resumed");
}, __("Status"));
}
@@ -713,9 +723,10 @@
});
},
- stop_work_order: function(frm, status) {
+ change_work_order_status: function(frm, status) {
+ let method_name = status=="Closed" ? "close_work_order" : "stop_unstop";
frappe.call({
- method: "erpnext.manufacturing.doctype.work_order.work_order.stop_unstop",
+ method: `erpnext.manufacturing.doctype.work_order.work_order.${method_name}`,
freeze: true,
freeze_message: __("Updating Work Order status"),
args: {
@@ -731,3 +742,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..12cd58f 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"
],
@@ -102,7 +99,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled",
+ "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nClosed\nCancelled",
"read_only": 1,
"reqd": 1,
"search_index": 1
@@ -185,6 +182,7 @@
"reqd": 1
},
{
+ "default": "1.0",
"fieldname": "qty",
"fieldtype": "Float",
"label": "Qty To Manufacture",
@@ -328,6 +326,7 @@
"label": "Expected Delivery Date"
},
{
+ "collapsible": 1,
"fieldname": "operations_section",
"fieldtype": "Section Break",
"label": "Operations",
@@ -339,7 +338,7 @@
"fieldname": "transfer_material_against",
"fieldtype": "Select",
"label": "Transfer Material Against",
- "options": "\nWork Order\nJob Card"
+ "options": "Work Order\nJob Card"
},
{
"fieldname": "operations",
@@ -553,23 +552,33 @@
"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-11-08 17:36:07.016300",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
+ "naming_rule": "By \"Naming Series\" field",
"nsm_parent_field": "parent_work_order",
"owner": "Administrator",
"permissions": [
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 5fe9fec..0090f4d 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
@@ -157,7 +175,7 @@
def update_status(self, status=None):
'''Update status of work order if unknown'''
- if status != "Stopped":
+ if status != "Stopped" and status != "Closed":
status = self.get_status(status)
if status != self.status:
@@ -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
@@ -589,7 +624,6 @@
def validate_operation_time(self):
for d in self.operations:
if not d.time_in_mins > 0:
- print(self.bom_no, self.production_item)
frappe.throw(_("Operation Time must be greater than 0 for Operation {0}").format(d.operation))
def update_required_items(self):
@@ -650,9 +684,7 @@
if not d.operation:
d.operation = operation
else:
- # Attribute a big number (999) to idx for sorting putpose in case idx is NULL
- # For instance in BOM Explosion Item child table, the items coming from sub assembly items
- for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999):
+ for item in sorted(item_dict.values(), key=lambda d: d['idx'] or float('inf')):
self.append('required_items', {
'rate': item.rate,
'amount': item.rate * item.qty,
@@ -934,6 +966,10 @@
frappe.throw(_("Not permitted"), frappe.PermissionError)
pro_order = frappe.get_doc("Work Order", work_order)
+
+ if pro_order.status == "Closed":
+ frappe.throw(_("Closed Work Order can not be stopped or Re-opened"))
+
pro_order.update_status(status)
pro_order.update_planned_qty()
frappe.msgprint(_("Work Order has been {0}").format(status))
@@ -968,6 +1004,29 @@
if row.job_card_qty > 0:
create_job_card(work_order, row, auto_create=True)
+@frappe.whitelist()
+def close_work_order(work_order, status):
+ if not frappe.has_permission("Work Order", "write"):
+ frappe.throw(_("Not permitted"), frappe.PermissionError)
+
+ work_order = frappe.get_doc("Work Order", work_order)
+ if work_order.get("operations"):
+ job_cards = frappe.get_list("Job Card",
+ filters={
+ "work_order": work_order.name,
+ "status": "Work In Progress"
+ }, pluck='name')
+
+ if job_cards:
+ job_cards = ", ".join(job_cards)
+ frappe.throw(_("Can not close Work Order. Since {0} Job Cards are in Work In Progress state.").format(job_cards))
+
+ work_order.update_status(status)
+ work_order.update_planned_qty()
+ frappe.msgprint(_("Work Order has been {0}").format(status))
+ work_order.notify_update()
+ return work_order.status
+
def split_qty_based_on_batch_size(wo_doc, row, qty):
if not cint(frappe.db.get_value("Operation",
row.operation, "create_job_card_based_on_batch_size")):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
index 403d46d..37dd11a 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'work_order',
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 9aa53b5..4311d3b 100644
--- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py
+++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class WorkOrderItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
index f7b8787..4e1a464 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
@@ -6,27 +6,27 @@
"field_order": [
"details",
"operation",
- "bom",
- "column_break_4",
- "description",
- "sequence_id",
- "col_break1",
- "completed_qty",
"status",
+ "completed_qty",
+ "column_break_4",
+ "bom",
"workstation",
+ "sequence_id",
+ "section_break_10",
+ "description",
"estimated_time_and_cost",
"planned_start_time",
- "planned_end_time",
- "column_break_10",
- "time_in_mins",
"hour_rate",
+ "time_in_mins",
+ "column_break_10",
+ "planned_end_time",
"batch_size",
"planned_operating_cost",
"section_break_9",
"actual_start_time",
- "actual_end_time",
- "column_break_11",
"actual_operation_time",
+ "column_break_11",
+ "actual_end_time",
"actual_operating_cost"
],
"fields": [
@@ -42,7 +42,6 @@
"oldfieldname": "operation_no",
"oldfieldtype": "Data",
"options": "Operation",
- "read_only": 1,
"reqd": 1
},
{
@@ -52,20 +51,14 @@
"label": "BOM",
"no_copy": 1,
"options": "BOM",
- "print_hide": 1,
- "read_only": 1
+ "print_hide": 1
},
{
"fieldname": "description",
"fieldtype": "Text Editor",
"label": "Operation Description",
"oldfieldname": "opn_description",
- "oldfieldtype": "Text",
- "read_only": 1
- },
- {
- "fieldname": "col_break1",
- "fieldtype": "Column Break"
+ "oldfieldtype": "Text"
},
{
"columns": 1,
@@ -74,19 +67,16 @@
"fieldtype": "Float",
"in_list_view": 1,
"label": "Completed Qty",
- "no_copy": 1,
- "read_only": 1
+ "no_copy": 1
},
{
"columns": 1,
"default": "Pending",
"fieldname": "status",
"fieldtype": "Select",
- "in_list_view": 1,
"label": "Status",
"no_copy": 1,
- "options": "Pending\nWork in Progress\nCompleted",
- "read_only": 1
+ "options": "Pending\nWork in Progress\nCompleted"
},
{
"fieldname": "workstation",
@@ -106,15 +96,13 @@
"fieldname": "planned_start_time",
"fieldtype": "Datetime",
"label": "Planned Start Time",
- "no_copy": 1,
- "read_only": 1
+ "no_copy": 1
},
{
"fieldname": "planned_end_time",
"fieldtype": "Datetime",
"label": "Planned End Time",
- "no_copy": 1,
- "read_only": 1
+ "no_copy": 1
},
{
"fieldname": "column_break_10",
@@ -122,7 +110,7 @@
},
{
"columns": 1,
- "description": "in Minutes",
+ "description": "In Minutes",
"fieldname": "time_in_mins",
"fieldtype": "Float",
"in_list_view": 1,
@@ -152,6 +140,7 @@
"label": "Actual Time and Cost"
},
{
+ "description": "Updated via 'Time Log' (In Minutes)",
"fieldname": "actual_start_time",
"fieldtype": "Datetime",
"label": "Actual Start Time",
@@ -159,7 +148,7 @@
"read_only": 1
},
{
- "description": "Updated via 'Time Log'",
+ "description": "Updated via 'Time Log' (In Minutes)",
"fieldname": "actual_end_time",
"fieldtype": "Datetime",
"label": "Actual End Time",
@@ -171,7 +160,7 @@
"fieldtype": "Column Break"
},
{
- "description": "in Minutes\nUpdated via 'Time Log'",
+ "description": "Updated via 'Time Log' (In Minutes)",
"fieldname": "actual_operation_time",
"fieldtype": "Float",
"label": "Actual Operation Time",
@@ -189,26 +178,30 @@
},
{
"fieldname": "batch_size",
- "fieldtype": "Int",
+ "fieldtype": "Float",
"label": "Batch Size",
"read_only": 1
},
{
"fieldname": "sequence_id",
"fieldtype": "Int",
+ "hidden": 1,
"label": "Sequence ID",
- "print_hide": 1,
- "read_only": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-24 14:36:12.835543",
+ "modified": "2021-11-29 16:37:18.824489",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Operation",
@@ -217,4 +210,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
+}
\ No newline at end of file
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..6bda58e 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.py
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class WorkOrderOperation(Document):
pass
diff --git a/erpnext/manufacturing/doctype/workstation/__init__.py b/erpnext/manufacturing/doctype/workstation/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/manufacturing/doctype/workstation/__init__.py
+++ b/erpnext/manufacturing/doctype/workstation/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index 9b73aca..5ed5153 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -1,14 +1,19 @@
# 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')
@@ -84,7 +89,7 @@
args = frappe._dict(args)
workstation_name = args.workstation_name or args.workstation
- try:
+ if not frappe.db.exists("Workstation", workstation_name):
doc = frappe.get_doc({
"doctype": "Workstation",
"workstation_name": workstation_name
@@ -94,5 +99,5 @@
doc.insert()
return doc
- except frappe.DuplicateEntryError:
- return frappe.get_doc("Workstation", workstation_name)
+
+ return frappe.get_doc("Workstation", workstation_name)
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js
index d8d25fc..5b9cedb 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.js
+++ b/erpnext/manufacturing/doctype/workstation/workstation.js
@@ -16,4 +16,29 @@
})
}
}
-})
+});
+
+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..4cfd410 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -1,14 +1,23 @@
# 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 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..bc481ca 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
@@ -1,6 +1,6 @@
-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..99fb552 100644
--- a/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.py
+++ b/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..02e3e93 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,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
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..25de2e0 100644
--- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
+++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
@@ -1,9 +1,9 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 8778d9b..e7a818a 100644
--- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py
+++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py
@@ -1,10 +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):
data = get_data(filters)
columns = get_columns(filters)
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..090a3e7 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -1,13 +1,14 @@
# 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.data import comma_and
+
def execute(filters=None):
-# if not filters: filters = {}
+ # if not filters: filters = {}
columns = get_columns()
summ_data = []
@@ -88,7 +89,7 @@
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
def get_manufacturer_records():
- details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
+ details = frappe.get_all('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
manufacture_details = frappe._dict()
for detail in details:
dic = manufacture_details.setdefault(detail.get('parent'), {})
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..fa94391 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
@@ -1,10 +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):
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..a5ae43e 100644
--- a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py
+++ b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py
@@ -1,10 +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(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 b4db98c..7741823 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
@@ -1,11 +1,11 @@
-# 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
from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
@@ -29,7 +29,6 @@
for row in job_cards:
row.operating_cost = flt(row.hour_rate) * (flt(row.total_time_in_mins) / 60.0)
- update_raw_material_cost(row, report_filters)
data.append(row)
return data
@@ -45,12 +44,6 @@
return filters
-def update_raw_material_cost(row, filters):
- row.rm_cost = 0.0
- for data in frappe.get_all("Job Card Item", fields = ["amount"],
- filters={"parent": row.name, "docstatus": 1}):
- row.rm_cost += data.amount
-
def get_columns(filters):
return [
{
@@ -58,7 +51,7 @@
"fieldtype": "Link",
"fieldname": "name",
"options": "Job Card",
- "width": "100"
+ "width": "120"
},
{
"label": _("Work Order"),
@@ -110,18 +103,12 @@
"label": _("Operating Cost"),
"fieldtype": "Currency",
"fieldname": "operating_cost",
- "width": "100"
- },
- {
- "label": _("Raw Material Cost"),
- "fieldtype": "Currency",
- "fieldname": "rm_cost",
- "width": "100"
+ "width": "150"
},
{
"label": _("Total Time (in Mins)"),
"fieldtype": "Float",
"fieldname": "total_time_in_mins",
- "width": "100"
+ "width": "150"
}
]
diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
index 74c794b..2c515d1 100644
--- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
+++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
@@ -1,10 +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.utils import flt
from frappe import _
+from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
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 9a6c764..26b3359 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
@@ -1,13 +1,16 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# 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()
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 a893905..4046bb1 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
@@ -1,11 +1,13 @@
# 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 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 = [], []
@@ -21,7 +23,7 @@
}
fields = ["name", "status", "work_order", "production_item", "item_name", "posting_date",
- "total_completed_qty", "workstation", "operation", "employee_name", "total_time_in_mins"]
+ "total_completed_qty", "workstation", "operation", "total_time_in_mins"]
for field in ["work_order", "workstation", "operation", "company"]:
if filters.get(field):
@@ -42,7 +44,7 @@
job_card_time_details = {}
for job_card_data in frappe.get_all("Job Card Time Log",
fields=["min(from_time) as from_time", "max(to_time) as to_time", "parent"],
- filters=job_card_time_filter, group_by="parent", debug=1):
+ filters=job_card_time_filter, group_by="parent"):
job_card_time_details[job_card_data.parent] = job_card_data
res = []
@@ -170,12 +172,6 @@
"width": 110
},
{
- "label": _("Employee Name"),
- "fieldname": "employee_name",
- "fieldtype": "Data",
- "width": 110
- },
- {
"label": _("Total Completed Qty"),
"fieldname": "total_completed_qty",
"fieldtype": "Float",
diff --git a/erpnext/healthcare/report/lab_test_report/__init__.py b/erpnext/manufacturing/report/process_loss_report/__init__.py
similarity index 100%
rename from erpnext/healthcare/report/lab_test_report/__init__.py
rename to erpnext/manufacturing/report/process_loss_report/__init__.py
diff --git a/erpnext/manufacturing/report/process_loss_report/process_loss_report.js b/erpnext/manufacturing/report/process_loss_report/process_loss_report.js
new file mode 100644
index 0000000..b0c2b94
--- /dev/null
+++ b/erpnext/manufacturing/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/manufacturing/report/process_loss_report/process_loss_report.json b/erpnext/manufacturing/report/process_loss_report/process_loss_report.json
new file mode 100644
index 0000000..7d3d13d
--- /dev/null
+++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.json
@@ -0,0 +1,26 @@
+{
+ "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-10-20 22:03:57.606612",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "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"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/process_loss_report/process_loss_report.py b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
new file mode 100644
index 0000000..9b544da
--- /dev/null
+++ b/erpnext/manufacturing/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)
+
+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/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py
index 42c9d97..d4743d3 100644
--- a/erpnext/manufacturing/report/production_analytics/production_analytics.py
+++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py
@@ -1,11 +1,13 @@
# 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 _, 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)
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..55b1a3f 100644
--- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
@@ -1,10 +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.utils import flt
+
def execute(filters=None):
columns, data = [], []
data = get_data(filters)
@@ -27,8 +28,15 @@
production_plan_doc = frappe.get_cached_doc("Production Plan", filters.get("production_plan"))
for row in production_plan_doc.po_items:
- work_order = frappe.get_cached_value("Work Order", {"production_plan_item": row.name,
- "bom_no": row.bom_no, "production_item": row.item_code}, "name")
+ work_order = frappe.get_value(
+ "Work Order",
+ {
+ "production_plan_item": row.name,
+ "bom_no": row.bom_no,
+ "production_item": row.item_code
+ },
+ "name"
+ )
if row.item_code not in itemwise_indent:
itemwise_indent.setdefault(row.item_code, {})
@@ -39,10 +47,10 @@
"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
"qty": row.planned_qty,
"document_type": "Work Order",
- "document_name": work_order,
+ "document_name": work_order or "",
"bom_level": frappe.get_cached_value("BOM", row.bom_no, "bom_level"),
- "produced_qty": order_details.get((work_order, row.item_code)).get("produced_qty"),
- "pending_qty": flt(row.planned_qty) - flt(order_details.get((work_order, row.item_code)).get("produced_qty"))
+ "produced_qty": order_details.get((work_order, row.item_code), {}).get("produced_qty", 0),
+ "pending_qty": flt(row.planned_qty) - flt(order_details.get((work_order, row.item_code), {}).get("produced_qty", 0))
})
get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details)
@@ -53,11 +61,23 @@
subcontracted_item = (item.type_of_manufacturing == 'Subcontract')
if subcontracted_item:
- docname = frappe.get_cached_value("Purchase Order Item",
- {"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "parent")
+ docname = frappe.get_value(
+ "Purchase Order Item",
+ {
+ "production_plan_sub_assembly_item": item.name,
+ "docstatus": ("<", 2)
+ },
+ "parent"
+ )
else:
- docname = frappe.get_cached_value("Work Order",
- {"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "name")
+ docname = frappe.get_value(
+ "Work Order",
+ {
+ "production_plan_sub_assembly_item": item.name,
+ "docstatus": ("<", 2)
+ },
+ "name"
+ )
data.append({
"indent": 1,
@@ -65,10 +85,10 @@
"item_name": item.item_name,
"qty": item.qty,
"document_type": "Work Order" if not subcontracted_item else "Purchase Order",
- "document_name": docname,
+ "document_name": docname or "",
"bom_level": item.bom_level,
- "produced_qty": order_details.get((docname, item.production_item)).get("produced_qty"),
- "pending_qty": flt(item.qty) - flt(order_details.get((docname, item.production_item)).get("produced_qty"))
+ "produced_qty": order_details.get((docname, item.production_item), {}).get("produced_qty", 0),
+ "pending_qty": flt(item.qty) - flt(order_details.get((docname, item.production_item), {}).get("produced_qty", 0))
})
def get_work_order_details(filters, order_details):
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..8368db6 100644
--- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py
+++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py
@@ -1,9 +1,10 @@
# 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 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 a12ac7f..a0c4a43 100644
--- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
+++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
@@ -1,10 +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 = [], []
data = get_data(filters)
diff --git a/erpnext/manufacturing/report/test_reports.py b/erpnext/manufacturing/report/test_reports.py
new file mode 100644
index 0000000..1de4726
--- /dev/null
+++ b/erpnext/manufacturing/report/test_reports.py
@@ -0,0 +1,64 @@
+import unittest
+from typing import List, Tuple
+
+import frappe
+
+from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report
+
+DEFAULT_FILTERS = {
+ "company": "_Test Company",
+ "from_date": "2010-01-01",
+ "to_date": "2030-01-01",
+ "warehouse": "_Test Warehouse - _TC",
+}
+
+
+REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [
+ ("BOM Explorer", {"bom": frappe.get_last_doc("BOM").name}),
+ ("BOM Operations Time", {}),
+ ("BOM Stock Calculated", {"bom": frappe.get_last_doc("BOM").name, "qty_to_make": 2}),
+ ("BOM Stock Report", {"bom": frappe.get_last_doc("BOM").name, "qty_to_produce": 2}),
+ ("Cost of Poor Quality Report", {}),
+ ("Downtime Analysis", {}),
+ (
+ "Exponential Smoothing Forecasting",
+ {
+ "based_on_document": "Sales Order",
+ "based_on_field": "Qty",
+ "no_of_years": 3,
+ "periodicity": "Yearly",
+ "smoothing_constant": 0.3,
+ },
+ ),
+ ("Job Card Summary", {"fiscal_year": "2021-2022"}),
+ ("Production Analytics", {"range": "Monthly"}),
+ ("Quality Inspection Summary", {}),
+ ("Process Loss Report", {}),
+ ("Work Order Stock Report", {}),
+ ("Work Order Summary", {"fiscal_year": "2021-2022", "age": 0}),
+]
+
+
+if frappe.db.a_row_exists("Production Plan"):
+ REPORT_FILTER_TEST_CASES.append(
+ ("Production Plan Summary", {"production_plan": frappe.get_last_doc("Production Plan").name})
+ )
+
+OPTIONAL_FILTERS = {
+ "warehouse": "_Test Warehouse - _TC",
+ "item": "_Test Item",
+ "item_group": "_Test Item Group",
+}
+
+
+class TestManufacturingReports(unittest.TestCase):
+ def test_execute_all_manufacturing_reports(self):
+ """Test that all script report in manufacturing modules are executable with supported filters"""
+ for report, filter in REPORT_FILTER_TEST_CASES:
+ execute_script_report(
+ report_name=report,
+ module="Manufacturing",
+ filters=filter,
+ default_filters=DEFAULT_FILTERS,
+ optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None,
+ )
diff --git a/erpnext/healthcare/doctype/diagnosis/__init__.py b/erpnext/manufacturing/report/work_order_consumed_materials/__init__.py
similarity index 100%
copy from erpnext/healthcare/doctype/diagnosis/__init__.py
copy to erpnext/manufacturing/report/work_order_consumed_materials/__init__.py
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js
new file mode 100644
index 0000000..b2428e8
--- /dev/null
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.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["Work Order Consumed Materials"] = {
+ "filters": [
+ {
+ label: __("Company"),
+ fieldname: "company",
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1
+ },
+ {
+ label: __("From Date"),
+ fieldname:"from_date",
+ fieldtype: "Date",
+ default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+ reqd: 1
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.get_today(),
+ reqd: 1
+ },
+ {
+ label: __("Work Order"),
+ fieldname: "name",
+ fieldtype: "Link",
+ options: "Work Order",
+ get_query: function() {
+ return {
+ filters: {
+ status: ["in", ["In Process", "Completed", "Stopped"]]
+ }
+ }
+ }
+ },
+ {
+ label: __("Production Item"),
+ fieldname: "production_item",
+ fieldtype: "Link",
+ depends_on: "eval: !doc.name",
+ options: "Item"
+ },
+ {
+ label: __("Status"),
+ fieldname: "status",
+ fieldtype: "Select",
+ options: ["In Process", "Completed", "Stopped"]
+ },
+ {
+ label: __("Excess Materials Consumed"),
+ fieldname: "show_extra_consumed_materials",
+ fieldtype: "Check"
+ }
+ ],
+ "formatter": function(value, row, column, data, default_formatter) {
+ value = default_formatter(value, row, column, data);
+
+ if (column.fieldname == "raw_material_name" && data && data.extra_consumed_qty > 0 ) {
+ value = `<div style="color:red">${value}</div>`;
+ }
+
+ return value;
+ },
+};
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.json b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.json
new file mode 100644
index 0000000..2fc986a
--- /dev/null
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.json
@@ -0,0 +1,30 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-11-22 17:36:11.886939",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "Gadgets International",
+ "modified": "2021-11-22 17:36:14.999091",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Work Order Consumed Materials",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Work Order",
+ "report_name": "Work Order Consumed Materials",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Manufacturing User"
+ },
+ {
+ "role": "Stock User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
new file mode 100644
index 0000000..0528348
--- /dev/null
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+
+
+def execute(filters=None):
+ columns, data = [], []
+ columns = get_columns()
+ data = get_data(filters)
+
+ return columns, data
+
+def get_data(report_filters):
+ fields = get_fields()
+ filters = get_filter_condition(report_filters)
+
+ wo_items = {}
+ for d in frappe.get_all("Work Order", filters = filters, fields=fields):
+ d.extra_consumed_qty = 0.0
+ if d.consumed_qty and d.consumed_qty > d.required_qty:
+ d.extra_consumed_qty = d.consumed_qty - d.required_qty
+
+ if d.extra_consumed_qty or not report_filters.show_extra_consumed_materials:
+ wo_items.setdefault((d.name, d.production_item), []).append(d)
+
+ data = []
+ for key, wo_data in wo_items.items():
+ for index, row in enumerate(wo_data):
+ if index != 0:
+ #If one work order has multiple raw materials then show parent data in the first row only
+ for field in ["name", "status", "production_item", "qty", "produced_qty"]:
+ row[field] = ""
+
+ data.append(row)
+
+ return data
+
+def get_fields():
+ return ["`tabWork Order Item`.`parent`", "`tabWork Order Item`.`item_code` as raw_material_item_code",
+ "`tabWork Order Item`.`item_name` as raw_material_name", "`tabWork Order Item`.`required_qty`",
+ "`tabWork Order Item`.`transferred_qty`", "`tabWork Order Item`.`consumed_qty`", "`tabWork Order`.`status`",
+ "`tabWork Order`.`name`", "`tabWork Order`.`production_item`", "`tabWork Order`.`qty`",
+ "`tabWork Order`.`produced_qty`"]
+
+def get_filter_condition(report_filters):
+ filters = {
+ "docstatus": 1, "status": ("in", ["In Process", "Completed", "Stopped"]),
+ "creation": ("between", [report_filters.from_date, report_filters.to_date])
+ }
+
+ for field in ["name", "production_item", "company", "status"]:
+ value = report_filters.get(field)
+ if value:
+ key = f"`{field}`"
+ filters.update({key: value})
+
+ return filters
+
+def get_columns():
+ return [
+ {
+ "label": _("Id"),
+ "fieldname": "name",
+ "fieldtype": "Link",
+ "options": "Work Order",
+ "width": 80
+ },
+ {
+ "label": _("Status"),
+ "fieldname": "status",
+ "fieldtype": "Data",
+ "width": 80
+ },
+ {
+ "label": _("Production Item"),
+ "fieldname": "production_item",
+ "fieldtype": "Link",
+ "options": "Item",
+ "width": 130
+ },
+ {
+ "label": _("Qty to Produce"),
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "width": 120
+ },
+ {
+ "label": _("Produced Qty"),
+ "fieldname": "produced_qty",
+ "fieldtype": "Float",
+ "width": 110
+ },
+ {
+ "label": _("Raw Material Item"),
+ "fieldname": "raw_material_item_code",
+ "fieldtype": "Link",
+ "options": "Item",
+ "width": 150
+ },
+ {
+ "label": _("Item Name"),
+ "fieldname": "raw_material_name",
+ "width": 130
+ },
+ {
+ "label": _("Required Qty"),
+ "fieldname": "required_qty",
+ "fieldtype": "Float",
+ "width": 100
+ },
+ {
+ "label": _("Transferred Qty"),
+ "fieldname": "transferred_qty",
+ "fieldtype": "Float",
+ "width": 100
+ },
+ {
+ "label": _("Consumed Qty"),
+ "fieldname": "consumed_qty",
+ "fieldtype": "Float",
+ "width": 100
+ },
+ {
+ "label": _("Extra Consumed Qty"),
+ "fieldname": "extra_consumed_qty",
+ "fieldtype": "Float",
+ "width": 100
+ }
+ ]
diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py
index 599a738..db0b239 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
@@ -1,9 +1,10 @@
# Copyright (c) 2017, Velometro Mobility Inc and contributors
# 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()
diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js
index eb23f17..832be23 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js
@@ -51,7 +51,7 @@
label: __("Status"),
fieldname: "status",
fieldtype: "Select",
- options: ["", "Not Started", "In Process", "Completed", "Stopped"]
+ options: ["", "Not Started", "In Process", "Completed", "Stopped", "Closed"]
},
{
label: __("Sales Orders"),
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 d0766f9..d7469dd 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
@@ -1,11 +1,14 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+from collections import defaultdict
+
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 = [], []
@@ -56,21 +59,16 @@
return get_chart_based_on_qty(data, filters)
def get_chart_based_on_status(data):
- labels = ["Completed", "In Process", "Stopped", "Not Started"]
+ labels = frappe.get_meta("Work Order").get_options("status").split("\n")
+ if "" in labels:
+ labels.remove("")
- status_wise_data = {
- "Not Started": 0,
- "In Process": 0,
- "Stopped": 0,
- "Completed": 0,
- "Draft": 0
- }
+ status_wise_data = defaultdict(int)
for d in data:
status_wise_data[d.status] += 1
- values = [status_wise_data["Completed"], status_wise_data["In Process"],
- status_wise_data["Stopped"], status_wise_data["Not Started"]]
+ values = [status_wise_data[label] for label in labels]
chart = {
"data": {
diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
index 84eabcd..65b4d02 100644
--- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
@@ -1,24 +1,13 @@
{
- "category": "",
- "charts": [
- {
- "chart_name": "Produced Quantity"
- }
- ],
- "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Manufacturing\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Plan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Forecasting\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM Stock Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Planning Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Production\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bill of Materials\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
+ "charts": [],
+ "content": "[{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order Summary\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-03-02 17:11:37.032604",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "organization",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Manufacturing",
"links": [
{
@@ -148,14 +137,6 @@
"type": "Link"
},
{
- "hidden": 0,
- "is_query_report": 0,
- "label": "Reports",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
"dependencies": "Work Order",
"hidden": 0,
"is_query_report": 1,
@@ -302,17 +283,131 @@
"link_type": "DocType",
"onboard": 0,
"type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Reports",
+ "link_count": 10,
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "Work Order",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Production Planning Report",
+ "link_count": 0,
+ "link_to": "Production Planning Report",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Work Order",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Work Order Summary",
+ "link_count": 0,
+ "link_to": "Work Order Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Quality Inspection",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Quality Inspection Summary",
+ "link_count": 0,
+ "link_to": "Quality Inspection Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Downtime Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Downtime Analysis",
+ "link_count": 0,
+ "link_to": "Downtime Analysis",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Job Card",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Job Card Summary",
+ "link_count": 0,
+ "link_to": "Job Card Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "BOM",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "BOM Search",
+ "link_count": 0,
+ "link_to": "BOM Search",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "BOM",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "BOM Stock Report",
+ "link_count": 0,
+ "link_to": "BOM Stock Report",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Work Order",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Production Analytics",
+ "link_count": 0,
+ "link_to": "Production Analytics",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "BOM",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "BOM Operations Time",
+ "link_count": 0,
+ "link_to": "BOM Operations Time",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Work Order Consumed Materials",
+ "link_count": 0,
+ "link_to": "Work Order Consumed Materials",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
}
],
- "modified": "2021-08-05 12:16:00.825741",
+ "modified": "2021-11-22 17:55:03.524496",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing",
- "onboarding": "Manufacturing",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "Manufacturing",
"roles": [],
diff --git a/erpnext/modules.txt b/erpnext/modules.txt
index 62f5dce..15a24a7 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -15,13 +15,11 @@
Maintenance
Education
Regional
-Healthcare
Restaurant
Agriculture
ERPNext Integrations
Non Profit
Hotels
-Hub Node
Quality Management
Communication
Loan Management
diff --git a/erpnext/non_profit/doctype/certification_application/certification_application.py b/erpnext/non_profit/doctype/certification_application/certification_application.py
index d4fc76b..cbbe191 100644
--- a/erpnext/non_profit/doctype/certification_application/certification_application.py
+++ b/erpnext/non_profit/doctype/certification_application/certification_application.py
@@ -1,10 +1,9 @@
-# -*- 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 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..8687b4d 100644
--- a/erpnext/non_profit/doctype/certification_application/test_certification_application.py
+++ b/erpnext/non_profit/doctype/certification_application/test_certification_application.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..47361cc 100644
--- a/erpnext/non_profit/doctype/certified_consultant/certified_consultant.py
+++ b/erpnext/non_profit/doctype/certified_consultant/certified_consultant.py
@@ -1,10 +1,9 @@
-# -*- 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 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..d10353c 100644
--- a/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.py
+++ b/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCertifiedConsultant(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/chapter/chapter.py b/erpnext/non_profit/doctype/chapter/chapter.py
index e9554b1..c01b1ef 100644
--- a/erpnext/non_profit/doctype/chapter/chapter.py
+++ b/erpnext/non_profit/doctype/chapter/chapter.py
@@ -1,11 +1,11 @@
-# -*- 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.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..98601ef 100644
--- a/erpnext/non_profit/doctype/chapter/test_chapter.py
+++ b/erpnext/non_profit/doctype/chapter/test_chapter.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 a1b25f2..80c0446 100644
--- a/erpnext/non_profit/doctype/chapter_member/chapter_member.py
+++ b/erpnext/non_profit/doctype/chapter_member/chapter_member.py
@@ -1,9 +1,9 @@
-# -*- 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 ChapterMember(Document):
pass
diff --git a/erpnext/non_profit/doctype/donation/donation.py b/erpnext/non_profit/doctype/donation/donation.py
index 9aa7e13..54bc94b 100644
--- a/erpnext/non_profit/doctype/donation/donation.py
+++ b/erpnext/non_profit/doctype/donation/donation.py
@@ -1,17 +1,18 @@
-# -*- 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
-import six
+
import json
-from frappe.model.document import Document
+
+import frappe
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):
@@ -81,7 +82,7 @@
notify_failure(log)
return { 'status': 'Failed', 'reason': e }
- if isinstance(data, six.string_types):
+ if isinstance(data, str):
data = json.loads(data)
data = frappe._dict(data)
@@ -167,7 +168,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
diff --git a/erpnext/non_profit/doctype/donation/donation_dashboard.py b/erpnext/non_profit/doctype/donation/donation_dashboard.py
index 3da8942..492ad62 100644
--- a/erpnext/non_profit/doctype/donation/donation_dashboard.py
+++ b/erpnext/non_profit/doctype/donation/donation_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'donation',
diff --git a/erpnext/non_profit/doctype/donation/test_donation.py b/erpnext/non_profit/doctype/donation/test_donation.py
index b206f54..5fa731a 100644
--- a/erpnext/non_profit/doctype/donation/test_donation.py
+++ b/erpnext/non_profit/doctype/donation/test_donation.py
@@ -1,12 +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
-import unittest
+
from erpnext.non_profit.doctype.donation.donation import create_donation
+
class TestDonation(unittest.TestCase):
def setUp(self):
create_donor_type()
diff --git a/erpnext/non_profit/doctype/donor/donor.py b/erpnext/non_profit/doctype/donor/donor.py
index ab6a197..058321b 100644
--- a/erpnext/non_profit/doctype/donor/donor.py
+++ b/erpnext/non_profit/doctype/donor/donor.py
@@ -1,10 +1,10 @@
-# -*- 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.contacts.address_and_contact import load_address_and_contact
+from frappe.model.document import Document
+
class Donor(Document):
def onload(self):
diff --git a/erpnext/non_profit/doctype/donor/test_donor.py b/erpnext/non_profit/doctype/donor/test_donor.py
index 3b6724e..fe591c8 100644
--- a/erpnext/non_profit/doctype/donor/test_donor.py
+++ b/erpnext/non_profit/doctype/donor/test_donor.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..17dca89 100644
--- a/erpnext/non_profit/doctype/donor_type/donor_type.py
+++ b/erpnext/non_profit/doctype/donor_type/donor_type.py
@@ -1,9 +1,9 @@
-# -*- 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 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..d433733 100644
--- a/erpnext/non_profit/doctype/donor_type/test_donor_type.py
+++ b/erpnext/non_profit/doctype/donor_type/test_donor_type.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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 b810fd0..cc5e1b1 100644
--- a/erpnext/non_profit/doctype/grant_application/grant_application.py
+++ b/erpnext/non_profit/doctype/grant_application/grant_application.py
@@ -1,13 +1,13 @@
-# -*- 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.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(
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..ef267d7 100644
--- a/erpnext/non_profit/doctype/grant_application/test_grant_application.py
+++ b/erpnext/non_profit/doctype/grant_application/test_grant_application.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestGrantApplication(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py
index 67828d6..4d80e57 100644
--- a/erpnext/non_profit/doctype/member/member.py
+++ b/erpnext/non_profit/doctype/member/member.py
@@ -1,16 +1,17 @@
-# -*- 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.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..0e31e3c 100644
--- a/erpnext/non_profit/doctype/member/member_dashboard.py
+++ b/erpnext/non_profit/doctype/member/member_dashboard.py
@@ -1,6 +1,6 @@
-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..46f14ed 100644
--- a/erpnext/non_profit/doctype/member/test_member.py
+++ b/erpnext/non_profit/doctype/member/test_member.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 88284f8..7151a3c 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -1,20 +1,20 @@
-# -*- 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 json
-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
+
+import frappe
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 import get_company_currency
+from erpnext.non_profit.doctype.member.member import create_member
+
class Membership(Document):
def validate(self):
@@ -208,7 +208,7 @@
try:
return frappe.get_doc("Member", members[0]["name"])
- except:
+ except Exception:
return None
@@ -343,7 +343,7 @@
notify_failure(log)
return {"status": "Failed", "reason": e}
- if isinstance(data, six.string_types):
+ if isinstance(data, str):
data = json.loads(data)
data = frappe._dict(data)
@@ -353,7 +353,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
@@ -394,7 +394,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
@@ -403,7 +403,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 5c876df..38a33a7 100644
--- a/erpnext/non_profit/doctype/membership/test_membership.py
+++ b/erpnext/non_profit/doctype/membership/test_membership.py
@@ -1,13 +1,15 @@
-# -*- coding: utf-8 -*-
# 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):
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.py b/erpnext/non_profit/doctype/membership_type/membership_type.py
index c712b99..b446421 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.py
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.py
@@ -1,11 +1,11 @@
-# -*- 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
+
import frappe
from frappe import _
+from frappe.model.document import Document
+
class MembershipType(Document):
def validate(self):
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..98bc087 100644
--- a/erpnext/non_profit/doctype/membership_type/test_membership_type.py
+++ b/erpnext/non_profit/doctype/membership_type/test_membership_type.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 50c9351..ace6605 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
@@ -1,13 +1,13 @@
-# -*- 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.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"):
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..51d1ba0 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
@@ -1,10 +1,9 @@
-# -*- 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 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..0a0ab2c 100644
--- a/erpnext/non_profit/doctype/volunteer/test_volunteer.py
+++ b/erpnext/non_profit/doctype/volunteer/test_volunteer.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..b44d67d 100644
--- a/erpnext/non_profit/doctype/volunteer/volunteer.py
+++ b/erpnext/non_profit/doctype/volunteer/volunteer.py
@@ -1,10 +1,10 @@
-# -*- 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.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..fe72518 100644
--- a/erpnext/non_profit/doctype/volunteer_skill/volunteer_skill.py
+++ b/erpnext/non_profit/doctype/volunteer_skill/volunteer_skill.py
@@ -1,10 +1,9 @@
-# -*- 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.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..cef27c8 100644
--- a/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.py
+++ b/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..3b1ae1a 100644
--- a/erpnext/non_profit/doctype/volunteer_type/volunteer_type.py
+++ b/erpnext/non_profit/doctype/volunteer_type/volunteer_type.py
@@ -1,9 +1,9 @@
-# -*- 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 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..3ddbfdc 100644
--- a/erpnext/non_profit/report/expiring_memberships/expiring_memberships.py
+++ b/erpnext/non_profit/report/expiring_memberships/expiring_memberships.py
@@ -1,9 +1,10 @@
# 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 _,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..02e3e93 100644
--- a/erpnext/non_profit/web_form/certification_application/certification_application.py
+++ b/erpnext/non_profit/web_form/certification_application/certification_application.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
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..02e3e93 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,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
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 186722a..3dfb381 100644
--- a/erpnext/non_profit/web_form/grant_application/grant_application.py
+++ b/erpnext/non_profit/web_form/grant_application/grant_application.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
def get_context(context):
context.no_cache = True
context.parents = [dict(label='View All ',
diff --git a/erpnext/non_profit/workspace/non_profit/non_profit.json b/erpnext/non_profit/workspace/non_profit/non_profit.json
index e6d4445..ba2f919 100644
--- a/erpnext/non_profit/workspace/non_profit/non_profit.json
+++ b/erpnext/non_profit/workspace/non_profit/non_profit.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Member\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Profit Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Membership\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter Member\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Grant Application\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Membership\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Volunteer\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Donation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tax Exemption Certification (India)\", \"col\": 4}}]",
"creation": "2020-03-02 17:23:47.811421",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "non-profit",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Non Profit",
"links": [
{
@@ -238,15 +231,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:01.146206",
+ "modified": "2021-08-05 12:16:01.146207",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Non Profit",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "Non Profit",
"roles": [],
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0a6a8bd..26fb859 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
@@ -61,13 +58,7 @@
erpnext.patches.v11_0.update_allow_transfer_for_manufacture
erpnext.patches.v11_0.add_item_group_defaults
erpnext.patches.v11_0.add_expense_claim_default_account
-execute:frappe.delete_doc("Page", "hub")
-erpnext.patches.v11_0.reset_publish_in_hub_for_all_items
-erpnext.patches.v11_0.update_hub_url # 2018-08-31 # 2018-09-03
erpnext.patches.v11_0.make_job_card
-erpnext.patches.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
@@ -158,7 +149,6 @@
erpnext.patches.v12_0.add_eway_bill_in_delivery_note
erpnext.patches.v12_0.set_lead_title_field
erpnext.patches.v12_0.set_permission_einvoicing
-erpnext.patches.v12_0.set_published_in_hub_tracked_item
erpnext.patches.v12_0.set_job_offer_applicant_email
erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
@@ -169,7 +159,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 +167,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 +184,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,6 +201,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
+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
@@ -221,7 +209,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
@@ -235,7 +222,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.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")
@@ -250,23 +236,20 @@
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.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.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
@@ -295,6 +278,41 @@
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.remove_bad_selling_defaults
+erpnext.patches.v13_0.migrate_stripe_api
erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
erpnext.patches.v13_0.einvoicing_deprecation_warning
+execute:frappe.reload_doc("erpnext_integrations", "doctype", "TaxJar Settings")
+execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Category")
erpnext.patches.v14_0.delete_einvoicing_doctypes
+erpnext.patches.v13_0.custom_fields_for_taxjar_integration #08-11-2021
+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 #27-11-2021
+erpnext.patches.v14_0.delete_shopify_doctypes
+erpnext.patches.v13_0.fix_invoice_statuses
+erpnext.patches.v13_0.replace_supplier_item_group_with_party_specific_item
+erpnext.patches.v13_0.update_dates_in_tax_withholding_category
+erpnext.patches.v14_0.update_opportunity_currency_fields
+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 #2
+erpnext.patches.v13_0.fix_additional_cost_in_mfg_stock_entry
+erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
+erpnext.patches.v13_0.add_default_interview_notification_templates
+erpnext.patches.v13_0.enable_scheduler_job_for_item_reposting
+erpnext.patches.v13_0.requeue_failed_reposts
+erpnext.patches.v13_0.update_job_card_status
+erpnext.patches.v12_0.update_production_plan_status
+erpnext.patches.v13_0.healthcare_deprecation_warning
+erpnext.patches.v13_0.item_naming_series_not_mandatory
+erpnext.patches.v14_0.delete_healthcare_doctypes
+erpnext.patches.v13_0.update_category_in_ltds_certificate
+erpnext.patches.v13_0.create_pan_field_for_india #2
+erpnext.patches.v14_0.delete_hub_doctypes
+erpnext.patches.v13_0.create_ksa_vat_custom_fields
+erpnext.patches.v14_0.migrate_crm_settings
+erpnext.patches.v13_0.rename_ksa_qr_field
+erpnext.patches.v13_0.disable_ksa_print_format_for_others
diff --git a/erpnext/patches/__init__.py b/erpnext/patches/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/patches/__init__.py
+++ b/erpnext/patches/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..165ca02 100644
--- a/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
+++ b/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
@@ -1,9 +1,9 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-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
deleted file mode 100644
index f6a1499..0000000
--- a/erpnext/patches/v10_0/delete_hub_documents.py
+++ /dev/null
@@ -1,19 +0,0 @@
-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")):
- frappe.delete_doc(dt, dn, ignore_missing=True)
-
- if frappe.db.exists("DocType", "Data Migration Plan"):
- data_migration_plans = frappe.get_all("Data Migration Plan", filters={"module": 'Hub Node'})
- for plan in data_migration_plans:
- plan_doc = frappe.get_doc("Data Migration Plan", plan.name)
- for m in plan_doc.get("mappings"):
- frappe.delete_doc("Data Migration Mapping", m.mapping, force=True)
- docs = frappe.get_all("Data Migration Run", filters={"data_migration_plan": plan.name})
- for doc in docs:
- frappe.delete_doc("Data Migration Run", doc.name)
- frappe.delete_doc("Data Migration Plan", plan.name)
diff --git a/erpnext/patches/v10_0/fichier_des_ecritures_comptables_for_france.py b/erpnext/patches/v10_0/fichier_des_ecritures_comptables_for_france.py
index 0315ae7..cdf5ba2 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
@@ -1,10 +1,12 @@
# Copyright (c) 2018, Frappe and Contributors
# 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/item_barcode_childtable_migrate.py b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
index ec9c6c3..ffff95d 100644
--- a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
+++ b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
@@ -1,7 +1,6 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
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 daa258e..fd51184 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
@@ -1,7 +1,7 @@
# Copyright (c) 2018, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
import frappe
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 f832936..a2deab6 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,6 +1,6 @@
-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)
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 a9dd310..525d1ff 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,7 @@
-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")
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..3f3d424 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,6 @@
-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..471f537 100644
--- a/erpnext/patches/v10_0/update_translatable_fields.py
+++ b/erpnext/patches/v10_0/update_translatable_fields.py
@@ -1,9 +1,6 @@
-#-*- coding: utf-8 -*-
-
-from __future__ import unicode_literals
-
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..6530b81 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,3 @@
-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..08006ad 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,3 @@
-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 0f1e496..fdf3046 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,9 @@
-from __future__ import unicode_literals
-import frappe, os
+import os
+
+import frappe
from frappe import _
+
def execute():
frappe.reload_doc("email", "doctype", "email_template")
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 a613bd8..f5658c5 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,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc("setup", "doctype", "company")
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 a45f39d..0000000
--- a/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py
+++ /dev/null
@@ -1,20 +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 0243dfb..7c99f58 100644
--- a/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py
+++ b/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py
@@ -1,9 +1,10 @@
# 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
+
def execute():
frappe.reload_doc("assets", "doctype", "Location")
for dt in ("Account", "Cost Center", "File", "Employee", "Location", "Task", "Customer Group", "Sales Person", "Territory"):
diff --git a/erpnext/patches/v11_0/add_item_group_defaults.py b/erpnext/patches/v11_0/add_item_group_defaults.py
index 2a15ad1..026047a 100644
--- a/erpnext/patches/v11_0/add_item_group_defaults.py
+++ b/erpnext/patches/v11_0/add_item_group_defaults.py
@@ -1,9 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# 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 a8841ef..6dcbf99 100644
--- a/erpnext/patches/v11_0/add_market_segments.py
+++ b/erpnext/patches/v11_0/add_market_segments.py
@@ -1,9 +1,8 @@
-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')
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 d06c688..064b721 100644
--- a/erpnext/patches/v11_0/add_sales_stages.py
+++ b/erpnext/patches/v11_0/add_sales_stages.py
@@ -1,8 +1,8 @@
-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')
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 0a1a360..5730212 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,6 @@
-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""")
diff --git a/erpnext/patches/v11_0/create_default_success_action.py b/erpnext/patches/v11_0/create_default_success_action.py
index 31feff2..e7b412c 100644
--- a/erpnext/patches/v11_0/create_default_success_action.py
+++ b/erpnext/patches/v11_0/create_default_success_action.py
@@ -1,7 +1,8 @@
-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..034418c 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,8 @@
-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 d3ea7a3..823eca1 100644
--- a/erpnext/patches/v11_0/create_salary_structure_assignments.py
+++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py
@@ -1,11 +1,16 @@
# Copyright (c) 2017, Frappe and Contributors
# 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')
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 029f75a..f0803cb 100644
--- a/erpnext/patches/v11_0/drop_column_max_days_allowed.py
+++ b/erpnext/patches/v11_0/drop_column_max_days_allowed.py
@@ -1,6 +1,6 @@
-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"):
diff --git a/erpnext/patches/v11_0/ewaybill_fields_gst_india.py b/erpnext/patches/v11_0/ewaybill_fields_gst_india.py
index 4247c78..5974e27 100644
--- a/erpnext/patches/v11_0/ewaybill_fields_gst_india.py
+++ b/erpnext/patches/v11_0/ewaybill_fields_gst_india.py
@@ -1,7 +1,8 @@
-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:
diff --git a/erpnext/patches/v11_0/hr_ux_cleanups.py b/erpnext/patches/v11_0/hr_ux_cleanups.py
index 8d18796..43c8504 100644
--- a/erpnext/patches/v11_0/hr_ux_cleanups.py
+++ b/erpnext/patches/v11_0/hr_ux_cleanups.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doctype('Employee')
frappe.db.sql('update tabEmployee set first_name = employee_name')
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..a1f1594 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,7 @@
-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 dfcf5ab..cd3869b 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
@@ -1,9 +1,9 @@
# Copyright (c) 2017, Frappe and Contributors
# 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')
diff --git a/erpnext/patches/v11_0/make_italian_localization_fields.py b/erpnext/patches/v11_0/make_italian_localization_fields.py
index 29d25c9..8ff23a5 100644
--- a/erpnext/patches/v11_0/make_italian_localization_fields.py
+++ b/erpnext/patches/v11_0/make_italian_localization_fields.py
@@ -1,11 +1,13 @@
# Copyright (c) 2017, Frappe and Contributors
# 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..120e018 100644
--- a/erpnext/patches/v11_0/make_job_card.py
+++ b/erpnext/patches/v11_0/make_job_card.py
@@ -1,10 +1,12 @@
# Copyright (c) 2017, Frappe and Contributors
# 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 8c92b51..ef6262b 100644
--- a/erpnext/patches/v11_0/make_location_from_warehouse.py
+++ b/erpnext/patches/v11_0/make_location_from_warehouse.py
@@ -1,10 +1,11 @@
# Copyright (c) 2017, Frappe and Contributors
# 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')
diff --git a/erpnext/patches/v11_0/make_quality_inspection_template.py b/erpnext/patches/v11_0/make_quality_inspection_template.py
index 9720af4..58c9fb9 100644
--- a/erpnext/patches/v11_0/make_quality_inspection_template.py
+++ b/erpnext/patches/v11_0/make_quality_inspection_template.py
@@ -1,9 +1,10 @@
# Copyright (c) 2017, Frappe and Contributors
# 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/merge_land_unit_with_location.py b/erpnext/patches/v11_0/merge_land_unit_with_location.py
index 7845da2..e1d0b12 100644
--- a/erpnext/patches/v11_0/merge_land_unit_with_location.py
+++ b/erpnext/patches/v11_0/merge_land_unit_with_location.py
@@ -1,7 +1,6 @@
# Copyright (c) 2018, Frappe and Contributors
# 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
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 6da70b4..bfc3fbc 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
@@ -1,9 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
import frappe
+
def execute():
'''
@@ -30,7 +31,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,
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 ef703d0..80e5ef7 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,7 @@
-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")
diff --git a/erpnext/patches/v11_0/rebuild_tree_for_company.py b/erpnext/patches/v11_0/rebuild_tree_for_company.py
index 4cb74c7..cad9c6c 100644
--- a/erpnext/patches/v11_0/rebuild_tree_for_company.py
+++ b/erpnext/patches/v11_0/rebuild_tree_for_company.py
@@ -1,7 +1,7 @@
-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 dd5cb63..1c4d8f1 100644
--- a/erpnext/patches/v11_0/refactor_autoname_naming.py
+++ b/erpnext/patches/v11_0/refactor_autoname_naming.py
@@ -1,7 +1,6 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import print_function, unicode_literals
import frappe
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
diff --git a/erpnext/patches/v11_0/refactor_naming_series.py b/erpnext/patches/v11_0/refactor_naming_series.py
index 9f231ed..e0aa004 100644
--- a/erpnext/patches/v11_0/refactor_naming_series.py
+++ b/erpnext/patches/v11_0/refactor_naming_series.py
@@ -1,7 +1,6 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import print_function, unicode_literals
import frappe
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
@@ -15,7 +14,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 +25,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 +39,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.-',
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..91f4bc5 100644
--- a/erpnext/patches/v11_0/remove_modules_setup_page.py
+++ b/erpnext/patches/v11_0/remove_modules_setup_page.py
@@ -1,8 +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 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..8fa876d 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,3 @@
-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 923b230..c7a3aa2 100644
--- a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
+++ b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
@@ -1,7 +1,7 @@
# 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
diff --git a/erpnext/patches/v11_0/rename_bom_wo_fields.py b/erpnext/patches/v11_0/rename_bom_wo_fields.py
index 0e6036b..cab7d0a 100644
--- a/erpnext/patches/v11_0/rename_bom_wo_fields.py
+++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py
@@ -1,10 +1,11 @@
# Copyright (c) 2018, Frappe and Contributors
# 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"
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..4b55aa0 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,7 @@
-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 06fc615..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')
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..95fb55d 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,6 @@
-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 fbf925d..c78ec5d 100644
--- a/erpnext/patches/v11_0/rename_overproduction_percent_field.py
+++ b/erpnext/patches/v11_0/rename_overproduction_percent_field.py
@@ -1,9 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# 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')
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..453a571 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
@@ -1,10 +1,11 @@
# Copyright (c) 2018, Frappe and Contributors
# 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..3f87550 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,9 @@
-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 d5ca4cc..f23a814 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
@@ -1,10 +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
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc('projects', 'doctype', 'project')
diff --git a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py b/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py
deleted file mode 100644
index 56e95e0..0000000
--- a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- frappe.reload_doc('stock', 'doctype', 'item')
- frappe.db.sql("""update `tabItem` set publish_in_hub = 0""")
diff --git a/erpnext/patches/v11_0/set_default_email_template_in_hr.py b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
index 4622376..ee083ca 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,6 @@
-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..b1098ab 100644
--- a/erpnext/patches/v11_0/set_department_for_doctypes.py
+++ b/erpnext/patches/v11_0/set_department_for_doctypes.py
@@ -1,4 +1,3 @@
-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 8f8a545..ec75d45 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,8 @@
-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:
diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py
index d8ce31f..99c3b0b 100644
--- a/erpnext/patches/v11_0/set_salary_component_properties.py
+++ b/erpnext/patches/v11_0/set_salary_component_properties.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc('Payroll', 'doctype', 'salary_detail')
frappe.reload_doc('Payroll', 'doctype', 'salary_component')
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..a44daaa 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,7 @@
-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 2f90f14..bb7ef87 100644
--- a/erpnext/patches/v11_0/set_user_permissions_for_department.py
+++ b/erpnext/patches/v11_0/set_user_permissions_for_department.py
@@ -1,6 +1,6 @@
-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)
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 4e72917..d387577 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,3 @@
-from __future__ import unicode_literals
import frappe
from frappe.desk.form.linked_with import get_linked_doctypes
diff --git a/erpnext/patches/v11_0/uom_conversion_data.py b/erpnext/patches/v11_0/uom_conversion_data.py
index 91470b3..81e547b 100644
--- a/erpnext/patches/v11_0/uom_conversion_data.py
+++ b/erpnext/patches/v11_0/uom_conversion_data.py
@@ -1,5 +1,5 @@
-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 dabaeff..c66cef0 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
@@ -1,9 +1,10 @@
# Copyright (c) 2017, 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', 'party_type')
party_types = {'Customer': 'Receivable', 'Supplier': 'Payable',
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 799e91a..3e36a4b 100644
--- a/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py
+++ b/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py
@@ -1,9 +1,10 @@
# 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.reload_doc('stock', 'doctype', 'item')
frappe.db.sql(""" update `tabItem` set include_item_in_manufacturing = 1
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 37a616c..f3a2ac6 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
@@ -1,9 +1,10 @@
# Copyright (c) 2017, Frappe and Contributors
# 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')
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 977d84f..ce1df78 100644
--- a/erpnext/patches/v11_0/update_brand_in_item_price.py
+++ b/erpnext/patches/v11_0/update_brand_in_item_price.py
@@ -1,9 +1,10 @@
# 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
+
def execute():
frappe.reload_doc('stock', 'doctype', 'item_price')
diff --git a/erpnext/patches/v11_0/update_delivery_trip_status.py b/erpnext/patches/v11_0/update_delivery_trip_status.py
index 42f017e..35b9535 100755
--- a/erpnext/patches/v11_0/update_delivery_trip_status.py
+++ b/erpnext/patches/v11_0/update_delivery_trip_status.py
@@ -1,9 +1,10 @@
# Copyright (c) 2017, 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', '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 2b38203..aff8e15 100644
--- a/erpnext/patches/v11_0/update_department_lft_rgt.py
+++ b/erpnext/patches/v11_0/update_department_lft_rgt.py
@@ -1,9 +1,8 @@
-from __future__ import unicode_literals
-
import frappe
from frappe import _
from frappe.utils.nestedset import rebuild_tree
+
def execute():
""" assign lft and rgt appropriately """
frappe.reload_doc("hr", "doctype", "department")
diff --git a/erpnext/patches/v11_0/update_hub_url.py b/erpnext/patches/v11_0/update_hub_url.py
deleted file mode 100644
index 6c6ca3c..0000000
--- a/erpnext/patches/v11_0/update_hub_url.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- frappe.reload_doc('hub_node', 'doctype', 'Marketplace Settings')
- frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'marketplace_url', 'https://hubmarket.org')
diff --git a/erpnext/patches/v11_0/update_sales_partner_type.py b/erpnext/patches/v11_0/update_sales_partner_type.py
index b393926..ef58499f 100644
--- a/erpnext/patches/v11_0/update_sales_partner_type.py
+++ b/erpnext/patches/v11_0/update_sales_partner_type.py
@@ -1,7 +1,7 @@
-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..4e807b4 100644
--- a/erpnext/patches/v11_0/update_total_qty_field.py
+++ b/erpnext/patches/v11_0/update_total_qty_field.py
@@ -1,6 +1,6 @@
-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 2892674..9b5c169 100644
--- a/erpnext/patches/v11_1/delete_bom_browser.py
+++ b/erpnext/patches/v11_1/delete_bom_browser.py
@@ -1,8 +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 frappe
+
def execute():
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..6f3da92 100644
--- a/erpnext/patches/v11_1/delete_scheduling_tool.py
+++ b/erpnext/patches/v11_1/delete_scheduling_tool.py
@@ -1,9 +1,10 @@
# 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
+
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 b706e5c..100cd64 100644
--- a/erpnext/patches/v11_1/make_job_card_time_logs.py
+++ b/erpnext/patches/v11_1/make_job_card_time_logs.py
@@ -1,9 +1,10 @@
# 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
+
def execute():
frappe.reload_doc('manufacturing', 'doctype', 'job_card_time_log')
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 fc3ec74..d292d7a 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
@@ -1,9 +1,10 @@
# 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
+
def execute():
frappe.reload_doctype("Quotation")
frappe.db.sql(""" UPDATE `tabQuotation` set party_name = lead WHERE quotation_to = 'Lead' """)
diff --git a/erpnext/patches/v11_1/rename_depends_on_lwp.py b/erpnext/patches/v11_1/rename_depends_on_lwp.py
index 4c4b14f..4e71838 100644
--- a/erpnext/patches/v11_1/rename_depends_on_lwp.py
+++ b/erpnext/patches/v11_1/rename_depends_on_lwp.py
@@ -1,11 +1,12 @@
# 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 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):
diff --git a/erpnext/patches/v11_1/renamed_delayed_item_report.py b/erpnext/patches/v11_1/renamed_delayed_item_report.py
index 8e8725c..c160b79 100644
--- a/erpnext/patches/v11_1/renamed_delayed_item_report.py
+++ b/erpnext/patches/v11_1/renamed_delayed_item_report.py
@@ -1,9 +1,10 @@
# 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
+
def execute():
for report in ["Delayed Order Item Summary", "Delayed Order Summary"]:
if frappe.db.exists("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..672b762 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
@@ -1,9 +1,10 @@
# 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
+
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..beec63a 100644
--- a/erpnext/patches/v11_1/set_missing_opportunity_from.py
+++ b/erpnext/patches/v11_1/set_missing_opportunity_from.py
@@ -1,6 +1,6 @@
-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..ac082b1 100644
--- a/erpnext/patches/v11_1/set_salary_details_submittable.py
+++ b/erpnext/patches/v11_1/set_salary_details_submittable.py
@@ -1,6 +1,6 @@
-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 ec01fbb..0d4f3d2 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,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.db.sql("""
update `tabMaterial Request`
diff --git a/erpnext/patches/v11_1/set_variant_based_on.py b/erpnext/patches/v11_1/set_variant_based_on.py
index 49a9a17..2e06e63 100644
--- a/erpnext/patches/v11_1/set_variant_based_on.py
+++ b/erpnext/patches/v11_1/set_variant_based_on.py
@@ -1,9 +1,10 @@
# 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
+
def execute():
frappe.db.sql("""update tabItem set variant_based_on = 'Item Attribute'
where ifnull(variant_based_on, '') = ''
diff --git a/erpnext/patches/v11_1/setup_guardian_role.py b/erpnext/patches/v11_1/setup_guardian_role.py
index 6ccfed9..dd9c1d2 100644
--- a/erpnext/patches/v11_1/setup_guardian_role.py
+++ b/erpnext/patches/v11_1/setup_guardian_role.py
@@ -1,6 +1,6 @@
-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 354e636..9b8be3d 100644
--- a/erpnext/patches/v11_1/update_bank_transaction_status.py
+++ b/erpnext/patches/v11_1/update_bank_transaction_status.py
@@ -1,9 +1,10 @@
# 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
+
def execute():
frappe.reload_doc("accounts", "doctype", "bank_transaction")
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 8c360ad..902df20 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
@@ -1,9 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# 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,
diff --git a/erpnext/patches/v11_1/woocommerce_set_creation_user.py b/erpnext/patches/v11_1/woocommerce_set_creation_user.py
index 074b904..19958ee 100644
--- a/erpnext/patches/v11_1/woocommerce_set_creation_user.py
+++ b/erpnext/patches/v11_1/woocommerce_set_creation_user.py
@@ -1,7 +1,7 @@
-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")
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 855d21d..80187d8 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
@@ -1,11 +1,11 @@
# Copyright (c) 2019, Frappe and Contributors
# 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("setup", "doctype", "company")
if frappe.db.has_column('Company', 'default_terms'):
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 6fe578d..f860cb4 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,6 @@
-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'})
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 cf1ed36..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'})
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 a0b1f87..dc9e884 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,8 @@
-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'})
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 c908192..6316bb3 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,7 @@
-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:
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 2e42368..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:
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..b0ddf00 100644
--- a/erpnext/patches/v12_0/add_taxjar_integration_field.py
+++ b/erpnext/patches/v12_0/add_taxjar_integration_field.py
@@ -1,6 +1,5 @@
-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 f171542..aec9cb8 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,7 @@
-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')
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 93d2576..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()
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 23a8f24..efcc7cf 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,8 @@
-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)
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 a6230f4..d157aad 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,10 @@
-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:
diff --git a/erpnext/patches/v12_0/create_taxable_value_field.py b/erpnext/patches/v12_0/create_taxable_value_field.py
index b9ee81d..55717cc 100644
--- a/erpnext/patches/v12_0/create_taxable_value_field.py
+++ b/erpnext/patches/v12_0/create_taxable_value_field.py
@@ -1,7 +1,7 @@
-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:
diff --git a/erpnext/patches/v12_0/delete_priority_property_setter.py b/erpnext/patches/v12_0/delete_priority_property_setter.py
index 1638557..cacc463 100644
--- a/erpnext/patches/v12_0/delete_priority_property_setter.py
+++ b/erpnext/patches/v12_0/delete_priority_property_setter.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.db.sql("""
DELETE FROM `tabProperty Setter`
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 ac7e82d..e5c4b8c 100644
--- a/erpnext/patches/v12_0/fix_quotation_expired_status.py
+++ b/erpnext/patches/v12_0/fix_quotation_expired_status.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
# fixes status of quotations which have status 'Expired' despite having valid sales order created
diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
index fe072d7..90e46d0 100644
--- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
@@ -1,10 +1,11 @@
# Copyright (c) 2018, Frappe and Contributors
# 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..d66f429 100644
--- a/erpnext/patches/v12_0/make_item_manufacturer.py
+++ b/erpnext/patches/v12_0/make_item_manufacturer.py
@@ -1,9 +1,10 @@
# Copyright (c) 2017, Frappe and Contributors
# 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..b3ee340 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,6 @@
-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..82dfba5 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
@@ -1,9 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# 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..5de7e69 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
@@ -1,9 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# 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..905aebe 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,8 +1,9 @@
-import frappe
import json
-from six import iteritems
+
+import frappe
from frappe.model.naming import make_autoname
+
def execute():
if "tax_type" not in frappe.db.get_table_columns("Item Tax"):
return
@@ -82,7 +83,7 @@
def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None, tax_types=None):
# search for previously created item tax template by comparing tax maps
- for template, item_tax_template_map in iteritems(item_tax_templates):
+ for template, item_tax_template_map in item_tax_templates.items():
if item_tax_map == item_tax_template_map:
return template
@@ -90,9 +91,10 @@
item_tax_template = frappe.new_doc("Item Tax Template")
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)
+ for tax_type, tax_rate in item_tax_map.items():
+ account_details = frappe.db.get_value("Account", tax_type, ['name', 'account_type', 'company'], as_dict=1)
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..c396891 100644
--- a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
+++ b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
@@ -1,7 +1,7 @@
# Copyright (c) 2017, Frappe and Contributors
# 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 97badf3..36fe18d 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
@@ -1,11 +1,13 @@
# Copyright (c) 2017, 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", "target_detail")
+ frappe.reload_doc("core", "doctype", "prepared_report")
for d in ['Sales Person', 'Sales Partner', 'Territory']:
frappe.db.sql("""
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 46794be..9b083ca 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,7 @@
-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("""
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 be884f9..12768a6 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,5 @@
-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")
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 4fcffb7..d1d4bcc 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
@@ -1,9 +1,9 @@
# Copyright (c) 2018, Frappe and Contributors
# 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
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 6b1b601..6ad68cc 100644
--- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
@@ -1,9 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# 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')
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..e02bf62 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,8 @@
# 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 9a08ad4..e33a1d0 100644
--- a/erpnext/patches/v12_0/rename_account_type_doctype.py
+++ b/erpnext/patches/v12_0/rename_account_type_doctype.py
@@ -1,6 +1,6 @@
-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)
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 7489ea3..a5d986a 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
@@ -1,10 +1,11 @@
# Copyright (c) 2019, Frappe and Contributors
# 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"):
diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation.py b/erpnext/patches/v12_0/rename_bank_reconciliation.py
index 2efa854..51ff0c8 100644
--- a/erpnext/patches/v12_0/rename_bank_reconciliation.py
+++ b/erpnext/patches/v12_0/rename_bank_reconciliation.py
@@ -1,9 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.table_exists("Bank Reconciliation"):
frappe.rename_doc('DocType', 'Bank Reconciliation', 'Bank Clearance', force=True)
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 c71b91c..96ae979 100644
--- a/erpnext/patches/v12_0/rename_lost_reason_detail.py
+++ b/erpnext/patches/v12_0/rename_lost_reason_detail.py
@@ -1,6 +1,6 @@
-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")
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 9d8626b..f5f0112 100644
--- a/erpnext/patches/v12_0/rename_pos_closing_doctype.py
+++ b/erpnext/patches/v12_0/rename_pos_closing_doctype.py
@@ -1,8 +1,9 @@
# 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"):
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..87630fb 100644
--- a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
+++ b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
@@ -1,7 +1,7 @@
# Copyright (c) 2017, Frappe and Contributors
# 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 20b0963..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")
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 f88a22f..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')
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 c52f380..198963d 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
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe and Contributors
# 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")
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 b5d7e3d..8f29fc8 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,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "accounts_settings")
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 4415cfe..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,4 +1,6 @@
import frappe
+
+
def execute():
frappe.reload_doc('hr', 'doctype', 'expense_claim_detail')
frappe.db.sql("""
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 13110df..d1e0e45 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,3 @@
-from __future__ import unicode_literals
import frappe
from frappe.utils import cint
diff --git a/erpnext/patches/v12_0/set_default_homepage_type.py b/erpnext/patches/v12_0/set_default_homepage_type.py
index a290e31..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')
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 038bd6d..de641c6 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,6 @@
-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")
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 a27c7b2..50d9fee 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,6 +1,5 @@
-from __future__ import unicode_literals
import frappe
-from six import iteritems
+
def execute():
frappe.reload_doctype('Landed Cost Taxes and Charges')
@@ -9,7 +8,7 @@
SELECT name, expenses_included_in_valuation from `tabCompany`
"""))
- for company, account in iteritems(company_account_map):
+ for company, account in company_account_map.items():
frappe.db.sql("""
UPDATE
`tabLanded Cost Taxes and Charges` t, `tabLanded Cost Voucher` l
diff --git a/erpnext/patches/v12_0/set_gst_category.py b/erpnext/patches/v12_0/set_gst_category.py
index cc09395..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'})
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 8fdc73b..b942fa4 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
@@ -1,10 +1,12 @@
# Copyright (c) 2017, Frappe and Contributors
# 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]
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 a5c8f75..a8e0ec1 100644
--- a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py
+++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py
@@ -1,10 +1,9 @@
# Copyright (c) 2017, Frappe and Contributors
# 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')
diff --git a/erpnext/patches/v12_0/set_payment_entry_status.py b/erpnext/patches/v12_0/set_payment_entry_status.py
index 84645a3..f879295 100644
--- a/erpnext/patches/v12_0/set_payment_entry_status.py
+++ b/erpnext/patches/v12_0/set_payment_entry_status.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doctype("Payment Entry")
frappe.db.sql("""update `tabPayment Entry` set status = CASE
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 66696be..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")
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 6c11cb4..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')
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 babaebe..bd2f7e2 100644
--- a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
+++ b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc("manufacturing", "doctype", "workstation")
diff --git a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py b/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py
deleted file mode 100644
index e54c7f3..0000000
--- a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- frappe.reload_doc("Hub Node", "doctype", "Hub Tracked Item")
- if not frappe.db.a_row_exists("Hub Tracked Item"):
- return
-
- frappe.db.sql('''
- Update `tabHub Tracked Item`
- SET published = 1
- ''')
diff --git a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py
index 52c9a2d..a15166e 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,8 @@
-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 87643a2..91e77e4 100644
--- a/erpnext/patches/v12_0/set_quotation_status.py
+++ b/erpnext/patches/v12_0/set_quotation_status.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.db.sql(""" UPDATE `tabQuotation` set status = 'Open'
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..d41134d 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,6 @@
-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..8c136e6 100644
--- a/erpnext/patches/v12_0/set_serial_no_status.py
+++ b/erpnext/patches/v12_0/set_serial_no_status.py
@@ -1,7 +1,7 @@
-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 1cc37ca..300d0f2 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
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc("stock", "doctype", "pick_list")
frappe.db.sql("""UPDATE `tabPick List` set purpose = 'Delivery'
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..154d7ba 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,6 @@
-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/stock_entry_enhancements.py b/erpnext/patches/v12_0/stock_entry_enhancements.py
index 17fdcd9..94d8ff9 100644
--- a/erpnext/patches/v12_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v12_0/stock_entry_enhancements.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
-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()
diff --git a/erpnext/patches/v12_0/unhide_cost_center_field.py b/erpnext/patches/v12_0/unhide_cost_center_field.py
index 3474a34..7245021 100644
--- a/erpnext/patches/v12_0/unhide_cost_center_field.py
+++ b/erpnext/patches/v12_0/unhide_cost_center_field.py
@@ -1,9 +1,10 @@
# Copyright (c) 2017, Frappe and Contributors
# 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`
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..5b5f623 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,3 @@
-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..64a2e41 100644
--- a/erpnext/patches/v12_0/update_address_template_for_india.py
+++ b/erpnext/patches/v12_0/update_address_template_for_india.py
@@ -1,10 +1,12 @@
# 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.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 f451664..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)
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 8a87171..37d850f 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,6 @@
-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")
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..e4418b0 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,6 @@
-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 c45f622..ef4204f 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,7 @@
-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')
diff --git a/erpnext/patches/v12_0/update_ewaybill_field_position.py b/erpnext/patches/v12_0/update_ewaybill_field_position.py
index 9e5f599..132fd90 100644
--- a/erpnext/patches/v12_0/update_ewaybill_field_position.py
+++ b/erpnext/patches/v12_0/update_ewaybill_field_position.py
@@ -1,6 +1,5 @@
-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'})
diff --git a/erpnext/patches/v12_0/update_gst_category.py b/erpnext/patches/v12_0/update_gst_category.py
index 1a54216..8b15370 100644
--- a/erpnext/patches/v12_0/update_gst_category.py
+++ b/erpnext/patches/v12_0/update_gst_category.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
diff --git a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
index d0b0443..f553ee9 100644
--- a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
+++ b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
@@ -1,7 +1,6 @@
-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': [
diff --git a/erpnext/patches/v12_0/update_is_cancelled_field.py b/erpnext/patches/v12_0/update_is_cancelled_field.py
index 4bbec44..df78750 100644
--- a/erpnext/patches/v12_0/update_is_cancelled_field.py
+++ b/erpnext/patches/v12_0/update_is_cancelled_field.py
@@ -1,6 +1,6 @@
-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 +11,5 @@
frappe.reload_doc("stock", "doctype", "stock_ledger_entry")
frappe.reload_doc("stock", "doctype", "serial_no")
- except:
+ 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 e15894d..abd4f6f 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,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'item_tax_template')
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 6ebaf48..e5f24d4 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,9 @@
-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
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 09f0707..ea3e002 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,9 @@
-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")
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..53c0ba5 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,6 @@
-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..b7c36ae 100644
--- a/erpnext/patches/v12_0/update_pricing_rule_fields.py
+++ b/erpnext/patches/v12_0/update_pricing_rule_fields.py
@@ -1,7 +1,7 @@
# Copyright (c) 2017, Frappe and Contributors
# 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_production_plan_status.py b/erpnext/patches/v12_0/update_production_plan_status.py
new file mode 100644
index 0000000..06fc503
--- /dev/null
+++ b/erpnext/patches/v12_0/update_production_plan_status.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+ frappe.reload_doc("manufacturing", "doctype", "production_plan")
+ frappe.db.sql("""
+ UPDATE `tabProduction Plan` ppl
+ SET status = "Completed"
+ WHERE ppl.name IN (
+ SELECT ss.name FROM (
+ SELECT
+ (
+ count(wo.status = "Completed") =
+ count(pp.name)
+ ) =
+ (
+ pp.status != "Completed"
+ AND pp.total_produced_qty >= pp.total_planned_qty
+ ) AS should_set,
+ pp.name AS name
+ FROM
+ `tabWork Order` wo INNER JOIN`tabProduction Plan` pp
+ ON wo.production_plan = pp.name
+ GROUP BY pp.name
+ HAVING should_set = 1
+ ) ss
+ )
+ """)
diff --git a/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py b/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
index 8dbfa18..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'})
diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py
index 24914fd..a09ac19 100644
--- a/erpnext/patches/v12_0/update_uom_conversion_factor.py
+++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py
@@ -1,5 +1,5 @@
-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/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..0208ca9
--- /dev/null
+++ b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
@@ -0,0 +1,35 @@
+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 cdc5a1e..8cee378 100644
--- a/erpnext/patches/v13_0/add_doctype_to_sla.py
+++ b/erpnext/patches/v13_0/add_doctype_to_sla.py
@@ -1,11 +1,11 @@
# Copyright (c) 2020, Frappe and Contributors
# 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('support', 'doctype', 'sla_fulfilled_on_status')
frappe.reload_doc('support', 'doctype', 'service_level_agreement')
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 a7b66f0..71abe2e 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,5 @@
-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")
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..7fbaffb 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,3 @@
-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..24141b7 100644
--- a/erpnext/patches/v13_0/add_standard_navbar_items.py
+++ b/erpnext/patches/v13_0/add_standard_navbar_items.py
@@ -1,7 +1,7 @@
-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..ee23747 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,6 @@
-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 1e4f383..23cad31 100644
--- a/erpnext/patches/v13_0/change_default_pos_print_format.py
+++ b/erpnext/patches/v13_0/change_default_pos_print_format.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.db.sql(
"""UPDATE `tabPOS Profile` profile
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 ebae3ad..5e1df14 100644
--- a/erpnext/patches/v13_0/check_is_income_tax_component.py
+++ b/erpnext/patches/v13_0/check_is_income_tax_component.py
@@ -1,10 +1,12 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-import frappe, erpnext
-from erpnext.regional.india.setup import setup
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+import erpnext
+
def execute():
@@ -19,16 +21,23 @@
]
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)
if erpnext.get_region() == "India":
- setup(patch=True)
+ create_custom_field('Salary Component',
+ dict(fieldname='component_type',
+ label='Component Type',
+ fieldtype='Select',
+ insert_after='description',
+ options='\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax',
+ depends_on='eval:doc.type == "Deduction"')
+ )
if frappe.db.exists("Salary Component", "Income Tax"):
frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)
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 341955a..bc64c63 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,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc('stock', 'doctype', 'quality_inspection_parameter')
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..4166945
--- /dev/null
+++ b/erpnext/patches/v13_0/create_gst_payment_entry_fields.py
@@ -0,0 +1,37 @@
+# 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')
+
+ if frappe.db.exists('Company', {'country': 'India'}):
+ 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)
+ else:
+ fields = ['gst_section', 'company_address', 'company_gstin', 'place_of_supply', 'customer_address', 'customer_gstin']
+ for field in fields:
+ frappe.delete_doc_if_exists("Custom Field", f"Payment Entry-{field}")
\ 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 08d4876..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'])
diff --git a/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py b/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py
new file mode 100644
index 0000000..f33b4b3
--- /dev/null
+++ b/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py
@@ -0,0 +1,12 @@
+import frappe
+
+from erpnext.regional.saudi_arabia.setup import make_custom_fields
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+ if not company:
+ return
+
+ make_custom_fields()
+
diff --git a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py
index 9a35453..5512543 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
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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)
diff --git a/erpnext/patches/v13_0/create_pan_field_for_india.py b/erpnext/patches/v13_0/create_pan_field_for_india.py
new file mode 100644
index 0000000..6df6e1e
--- /dev/null
+++ b/erpnext/patches/v13_0/create_pan_field_for_india.py
@@ -0,0 +1,29 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+
+def execute():
+ frappe.reload_doc('buying', 'doctype', 'supplier', force=True)
+ frappe.reload_doc('selling', 'doctype', 'customer', force=True)
+ frappe.reload_doc('core', 'doctype', 'doctype', force=True)
+
+ custom_fields = {
+ 'Supplier': [
+ {
+ 'fieldname': 'pan',
+ 'label': 'PAN',
+ 'fieldtype': 'Data',
+ 'insert_after': 'supplier_type'
+ }
+ ],
+ 'Customer': [
+ {
+ 'fieldname': 'pan',
+ 'label': 'PAN',
+ 'fieldtype': 'Data',
+ 'insert_after': 'customer_type'
+ }
+ ]
+ }
+
+ create_custom_fields(custom_fields, update=True)
diff --git a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
index 6ad3402..87c9cf1 100644
--- a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
+++ b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
@@ -1,11 +1,12 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-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:
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..078c558
--- /dev/null
+++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
@@ -0,0 +1,39 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+from erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings import add_permissions
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'United States'}, fields=['name'])
+ if not company:
+ return
+
+ TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+ TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
+ TAXJAR_SANDBOX_MODE = frappe.db.get_single_value("TaxJar Settings", "is_sandbox")
+
+ if (not TAXJAR_CREATE_TRANSACTIONS and not TAXJAR_CALCULATE_TAX and not TAXJAR_SANDBOX_MODE):
+ return
+
+ custom_fields = {
+ 'Sales Invoice Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
+ label='Product Tax Category', fetch_from='item_code.product_tax_category'),
+ dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
+ label='Tax Collectable', read_only=1, options='currency'),
+ dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
+ label='Taxable Amount', read_only=1, options='currency')
+ ],
+ 'Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
+ label='Product Tax Category')
+ ],
+ 'TaxJar Settings': [
+ dict(fieldname='company', fieldtype='Link', insert_after='configuration', options='Company',
+ label='Company')
+ ]
+ }
+ create_custom_fields(custom_fields, update=True)
+ add_permissions()
+ frappe.enqueue('erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings.add_product_tax_categories', now=True)
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..2c5c577 100644
--- a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
+++ b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
@@ -1,11 +1,11 @@
# Copyright (c) 2019, Frappe and Contributors
# 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():
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 c17aad0..e57d6d0 100644
--- a/erpnext/patches/v13_0/delete_old_purchase_reports.py
+++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py
@@ -1,10 +1,12 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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 +15,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)
diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py
index 671c012..c597fe8 100644
--- a/erpnext/patches/v13_0/delete_old_sales_reports.py
+++ b/erpnext/patches/v13_0/delete_old_sales_reports.py
@@ -1,16 +1,19 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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)
diff --git a/erpnext/patches/v13_0/delete_orphaned_tables.py b/erpnext/patches/v13_0/delete_orphaned_tables.py
index 50a4a0e..c32f830 100644
--- a/erpnext/patches/v13_0/delete_orphaned_tables.py
+++ b/erpnext/patches/v13_0/delete_orphaned_tables.py
@@ -1,11 +1,11 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
from frappe.utils import getdate
+
def execute():
frappe.reload_doc('setup', 'doctype', 'transaction_deletion_record')
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 8d6340d..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"])
diff --git a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
new file mode 100644
index 0000000..c815b3b
--- /dev/null
+++ b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2020, Wahni Green Technologies and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+ if company:
+ return
+
+ if frappe.db.exists('DocType', 'Print Format'):
+ frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
+ frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
+ for d in ('KSA VAT Invoice', 'KSA POS Invoice'):
+ frappe.db.set_value("Print Format", d, "disabled", 1)
diff --git a/erpnext/patches/v13_0/drop_razorpay_payload_column.py b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
index 76b8041..611ba7e 100644
--- a/erpnext/patches/v13_0/drop_razorpay_payload_column.py
+++ b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
@@ -1,6 +1,6 @@
-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"):
diff --git a/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py b/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py
new file mode 100644
index 0000000..7a51b43
--- /dev/null
+++ b/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py
@@ -0,0 +1,8 @@
+import frappe
+
+
+def execute():
+ frappe.reload_doc('core', 'doctype', 'scheduled_job_type')
+ if frappe.db.exists('Scheduled Job Type', 'repost_item_valuation.repost_entries'):
+ frappe.db.set_value('Scheduled Job Type',
+ 'repost_item_valuation.repost_entries', 'stopped', 0)
diff --git a/erpnext/patches/v13_0/fix_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_invoice_statuses.py b/erpnext/patches/v13_0/fix_invoice_statuses.py
new file mode 100644
index 0000000..4395757
--- /dev/null
+++ b/erpnext/patches/v13_0/fix_invoice_statuses.py
@@ -0,0 +1,113 @@
+import frappe
+from frappe.utils import flt, getdate
+
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+ get_total_in_party_account_currency,
+ is_overdue,
+)
+
+TODAY = getdate()
+
+def execute():
+ # This fix is not related to Party Specific Item,
+ # but it is needed for code introduced after Party Specific Item was
+ # If your DB doesn't have this doctype yet, you should be fine
+ if not frappe.db.exists("DocType", "Party Specific Item"):
+ return
+
+ for doctype in ("Purchase Invoice", "Sales Invoice"):
+ fields = [
+ "name",
+ "status",
+ "due_date",
+ "outstanding_amount",
+ "grand_total",
+ "base_grand_total",
+ "rounded_total",
+ "base_rounded_total",
+ "disable_rounded_total",
+ ]
+ if doctype == "Sales Invoice":
+ fields.append("is_pos")
+
+ invoices_to_update = frappe.get_all(
+ doctype,
+ fields=fields,
+ filters={
+ "docstatus": 1,
+ "status": ("in", (
+ "Overdue",
+ "Overdue and Discounted",
+ "Partly Paid",
+ "Partly Paid and Discounted"
+ )),
+ "outstanding_amount": (">", 0),
+ "modified": (">", "2021-01-01")
+ # an assumption is being made that only invoices modified
+ # after 2021 got affected as incorrectly overdue.
+ # required for performance reasons.
+ }
+ )
+
+ invoices_to_update = {
+ invoice.name: invoice for invoice in invoices_to_update
+ }
+
+ payment_schedule_items = frappe.get_all(
+ "Payment Schedule",
+ fields=(
+ "due_date",
+ "payment_amount",
+ "base_payment_amount",
+ "parent"
+ ),
+ filters={"parent": ("in", invoices_to_update)}
+ )
+
+ for item in payment_schedule_items:
+ invoices_to_update[item.parent].setdefault(
+ "payment_schedule", []
+ ).append(item)
+
+ status_map = {}
+
+ for invoice in invoices_to_update.values():
+ invoice.doctype = doctype
+ doc = frappe.get_doc(invoice)
+ correct_status = get_correct_status(doc)
+ if not correct_status or doc.status == correct_status:
+ continue
+
+ status_map.setdefault(correct_status, []).append(doc.name)
+
+ for status, docs in status_map.items():
+ frappe.db.set_value(
+ doctype, {"name": ("in", docs)},
+ "status",
+ status,
+ update_modified=False
+ )
+
+
+
+def get_correct_status(doc):
+ outstanding_amount = flt(
+ doc.outstanding_amount, doc.precision("outstanding_amount")
+ )
+ total = get_total_in_party_account_currency(doc)
+
+ status = ""
+ if is_overdue(doc, total):
+ status = "Overdue"
+ elif 0 < outstanding_amount < total:
+ status = "Partly Paid"
+ elif outstanding_amount > 0 and getdate(doc.due_date) >= TODAY:
+ status = "Unpaid"
+
+ if not status:
+ return
+
+ if doc.status.endswith(" and Discounted"):
+ status += " and Discounted"
+
+ return status
diff --git a/erpnext/patches/v13_0/fix_non_unique_represents_company.py b/erpnext/patches/v13_0/fix_non_unique_represents_company.py
index f20c73a..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,5 +1,6 @@
import frappe
+
def execute():
frappe.db.sql("""
update tabCustomer
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 dca43b4..72cda75 100644
--- a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
+++ b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
@@ -1,7 +1,6 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
diff --git a/erpnext/patches/v13_0/germany_make_custom_fields.py b/erpnext/patches/v13_0/germany_make_custom_fields.py
index 41ab945..80b6a39 100644
--- a/erpnext/patches/v13_0/germany_make_custom_fields.py
+++ b/erpnext/patches/v13_0/germany_make_custom_fields.py
@@ -1,9 +1,9 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-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..76f8b27
--- /dev/null
+++ b/erpnext/patches/v13_0/gst_fields_for_pos_invoice.py
@@ -0,0 +1,42 @@
+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..98ce12b 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,3 @@
-from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import rename_field
diff --git a/erpnext/patches/v13_0/item_naming_series_not_mandatory.py b/erpnext/patches/v13_0/item_naming_series_not_mandatory.py
new file mode 100644
index 0000000..5fe85a4
--- /dev/null
+++ b/erpnext/patches/v13_0/item_naming_series_not_mandatory.py
@@ -0,0 +1,11 @@
+import frappe
+
+from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
+
+
+def execute():
+
+ stock_settings = frappe.get_doc("Stock Settings")
+
+ set_by_naming_series("Item", "item_code",
+ stock_settings.get("item_naming_by")=="Naming Series", hide_name_field=True, make_mandatory=0)
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 c4ad1b7..0f2ac4b 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,14 +1,24 @@
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',
- 'purchase_invoice_item', 'delivery_note_item', 'sales_invoice_item', 'packed_item'):
- frappe.reload_doc('stock', 'doctype', doctype)
- frappe.reload_doc('buying', 'doctype', 'purchase_receipt_item_supplied')
+ doctypes_to_reload = [
+ ("stock", "repost_item_valuation"),
+ ("stock", "stock_entry_detail"),
+ ("stock", "purchase_receipt_item"),
+ ("stock", "delivery_note_item"),
+ ("stock", "packed_item"),
+ ("accounts", "sales_invoice_item"),
+ ("accounts", "purchase_invoice_item"),
+ ("buying", "purchase_receipt_item_supplied")
+ ]
+
+ for module, doctype in doctypes_to_reload:
+ frappe.reload_doc(module, 'doctype', doctype)
reposting_project_deployed_on = get_creation_time()
posting_date = getdate(reposting_project_deployed_on)
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 d2228c3..68bcd8a 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
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
+
def execute():
'''`sales_invoice` field from loyalty point entry is splitted into `invoice_type` & `invoice` fields'''
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 73361f0..a7bdf93 100644
--- a/erpnext/patches/v13_0/make_non_standard_user_type.py
+++ b/erpnext/patches/v13_0/make_non_standard_user_type.py
@@ -1,11 +1,12 @@
# Copyright (c) 2019, Frappe and Contributors
# 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'],
@@ -13,7 +14,7 @@
'hr': ['Employee', 'Expense Claim', 'Leave Application', 'Attendance Request', 'Compensatory Leave Request']
}
- for module, doctypes in iteritems(doctype_dict):
+ for module, doctypes in doctype_dict.items():
for doctype in doctypes:
frappe.reload_doc(module, 'doctype', doctype)
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..1c65998
--- /dev/null
+++ b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
@@ -0,0 +1,59 @@
+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, 0) != 0
+ 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, 0) != 0
+ 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")
+
+ acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
+ if acc_frozen_upto:
+ frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+
+ for invoice in purchase_invoices + sales_invoices:
+ try:
+ doc = frappe.get_doc(invoice.type, invoice.name)
+ doc.docstatus = 2
+ doc.make_gl_entries()
+ for advance in doc.advances:
+ if advance.ref_exchange_rate == 1:
+ advance.db_set('exchange_gain_loss', 0, False)
+ doc.docstatus = 1
+ doc.make_gl_entries()
+ frappe.db.commit()
+ except Exception:
+ frappe.db.rollback()
+ print(f'Failed to correct gl entries of {invoice.name}')
+
+ if acc_frozen_upto:
+ frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', acc_frozen_upto)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/move_branch_code_to_bank_account.py b/erpnext/patches/v13_0/move_branch_code_to_bank_account.py
index 24d9196..350744f 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
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'bank_account')
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..c07caae 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
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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..fca7c09 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
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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..d1ea22f 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
@@ -1,10 +1,9 @@
# Copyright (c) 2019, Frappe and Contributors
# 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 not (frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")):
@@ -86,7 +85,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 +107,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 15aeb76..7c10a31 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
@@ -1,7 +1,6 @@
-from __future__ import unicode_literals
-
import frappe
+
def execute():
if not frappe.db.table_exists("Additional Salary"):
return
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..3da6f74 100644
--- a/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
+++ b/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
@@ -1,10 +1,9 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-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/remove_bad_selling_defaults.py b/erpnext/patches/v13_0/remove_bad_selling_defaults.py
new file mode 100644
index 0000000..5487a6c
--- /dev/null
+++ b/erpnext/patches/v13_0/remove_bad_selling_defaults.py
@@ -0,0 +1,15 @@
+import frappe
+from frappe import _
+
+
+def execute():
+ selling_settings = frappe.get_single("Selling Settings")
+
+ if selling_settings.customer_group in (_("All Customer Groups"), "All Customer Groups"):
+ selling_settings.customer_group = None
+
+ if selling_settings.territory in (_("All Territories"), "All Territories"):
+ selling_settings.territory = None
+
+ selling_settings.flags.ignore_mandatory=True
+ selling_settings.save(ignore_permissions=True)
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..3bd717d 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,7 @@
-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..bf5438c 100644
--- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -1,10 +1,11 @@
# Copyright (c) 2020, Frappe and Contributors
# 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 +42,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 +60,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 4ef04ad..b129cbe 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
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe and Contributors
# 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")
diff --git a/erpnext/patches/v13_0/rename_ksa_qr_field.py b/erpnext/patches/v13_0/rename_ksa_qr_field.py
new file mode 100644
index 0000000..0bb86e0
--- /dev/null
+++ b/erpnext/patches/v13_0/rename_ksa_qr_field.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2020, Wahni Green Technologies and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+ if not company:
+ return
+
+ if frappe.db.exists('DocType', 'Sales Invoice'):
+ frappe.reload_doc('accounts', 'doctype', 'sales_invoice', force=True)
+ if frappe.db.has_column('Sales Invoice', 'qr_code'):
+ rename_field('Sales Invoice', 'qr_code', 'ksa_einv_qr')
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 f60567b..265e2a9 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,7 @@
-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")
diff --git a/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py b/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py
index 1787a56..813fbd2 100644
--- a/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py
+++ b/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py
@@ -1,23 +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',
+ 'HR Settings',
+ 'HR Settings',
+ 'send_birthday_reminders',
1 if old_value == 0 else 0
)
-
+
except Exception as e:
if e.args[0] != 1054:
- raise
\ No newline at end of file
+ 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 d8bcd7f..7d757b7 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,6 @@
-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)
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 bc1fc98..a2c960c 100644
--- a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
+++ b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "pos_payment_method")
pos_profiles = frappe.get_all("POS Profile")
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/requeue_failed_reposts.py b/erpnext/patches/v13_0/requeue_failed_reposts.py
new file mode 100644
index 0000000..213cb9e
--- /dev/null
+++ b/erpnext/patches/v13_0/requeue_failed_reposts.py
@@ -0,0 +1,13 @@
+import frappe
+from frappe.utils import cstr
+
+
+def execute():
+
+ reposts = frappe.get_all("Repost Item Valuation",
+ {"status": "Failed", "modified": [">", "2021-10-05"] },
+ ["name", "modified", "error_log"])
+
+ for repost in reposts:
+ if "check_freezing_date" in cstr(repost.error_log):
+ frappe.db.set_value("Repost Item Valuation", repost.name, "status", "Queued")
diff --git a/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py b/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py
index 1da5275..69fc6a2 100644
--- a/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py
+++ b/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py
@@ -1,10 +1,10 @@
# 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.
@@ -35,11 +35,11 @@
for payment_entry in intra_company_pe:
reconciled_bank_transactions[payment_entry] = frappe.get_all(
- 'Bank Transaction Payments',
+ 'Bank Transaction Payments',
filters = {
'payment_entry': payment_entry
- },
+ },
pluck='parent'
)
- return reconciled_bank_transactions
\ No newline at end of file
+ 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..f82a0d5 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,6 @@
-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 13ec41e..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,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doc('HR', 'doctype', 'Leave Allocation')
frappe.reload_doc('HR', 'doctype', 'Leave Ledger Entry')
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 7f75946..87b3389 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,6 @@
-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")
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 7968e74..6a3785d 100644
--- a/erpnext/patches/v13_0/set_pos_closing_as_failed.py
+++ b/erpnext/patches/v13_0/set_pos_closing_as_failed.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'pos_closing_entry')
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 3db183f..e44f321 100644
--- a/erpnext/patches/v13_0/set_training_event_attendance.py
+++ b/erpnext/patches/v13_0/set_training_event_attendance.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def execute():
frappe.reload_doc('hr', 'doctype', 'training_event')
frappe.reload_doc('hr', 'doctype', 'training_event_employee')
diff --git a/erpnext/patches/v13_0/set_youtube_video_id.py b/erpnext/patches/v13_0/set_youtube_video_id.py
index f6104d1..e1eb1b9 100644
--- a/erpnext/patches/v13_0/set_youtube_video_id.py
+++ b/erpnext/patches/v13_0/set_youtube_video_id.py
@@ -1,7 +1,8 @@
-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")
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 c8c160f..dc3f8aa 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,7 +1,8 @@
-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:
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..82cc1ff 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
@@ -1,9 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# 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 83581dd..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()
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/shopify_deprecation_warning.py b/erpnext/patches/v13_0/shopify_deprecation_warning.py
index 6f199c8..245d1a9 100644
--- a/erpnext/patches/v13_0/shopify_deprecation_warning.py
+++ b/erpnext/patches/v13_0/shopify_deprecation_warning.py
@@ -1,5 +1,4 @@
import click
-import frappe
def execute():
diff --git a/erpnext/patches/v13_0/stock_entry_enhancements.py b/erpnext/patches/v13_0/stock_entry_enhancements.py
index 7b93ce3..968a83a 100644
--- a/erpnext/patches/v13_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v13_0/stock_entry_enhancements.py
@@ -1,9 +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
+
import frappe
+
def execute():
frappe.reload_doc("stock", "doctype", "stock_entry")
if frappe.db.has_column("Stock Entry", "add_to_transit"):
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 50f233d..55fd465 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
@@ -1,12 +1,11 @@
-
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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")
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 dc9ed18..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."""
diff --git a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py
new file mode 100644
index 0000000..a5f5a23
--- /dev/null
+++ b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py
@@ -0,0 +1,20 @@
+import frappe
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ frappe.reload_doc('regional', 'doctype', 'lower_deduction_certificate')
+
+ ldc = frappe.qb.DocType("Lower Deduction Certificate").as_("ldc")
+ supplier = frappe.qb.DocType("Supplier")
+
+ frappe.qb.update(ldc).inner_join(supplier).on(
+ ldc.supplier == supplier.name
+ ).set(
+ ldc.tax_withholding_category, supplier.tax_withholding_category
+ ).where(
+ ldc.tax_withholding_category.isnull()
+ ).run()
\ No newline at end of file
diff --git a/erpnext/patches/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 bcc0952..1b63635 100644
--- a/erpnext/patches/v13_0/update_deferred_settings.py
+++ b/erpnext/patches/v13_0/update_deferred_settings.py
@@ -1,8 +1,9 @@
# 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'
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 ef70b55..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:
diff --git a/erpnext/patches/v13_0/update_job_card_details.py b/erpnext/patches/v13_0/update_job_card_details.py
index 733b3a9..12f9006 100644
--- a/erpnext/patches/v13_0/update_job_card_details.py
+++ b/erpnext/patches/v13_0/update_job_card_details.py
@@ -1,9 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# 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")
diff --git a/erpnext/patches/v13_0/update_job_card_status.py b/erpnext/patches/v13_0/update_job_card_status.py
new file mode 100644
index 0000000..797a3e2
--- /dev/null
+++ b/erpnext/patches/v13_0/update_job_card_status.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+
+ job_card = frappe.qb.DocType("Job Card")
+ (frappe.qb
+ .update(job_card)
+ .set(job_card.status, "Completed")
+ .where(
+ (job_card.docstatus == 1)
+ & (job_card.for_quantity <= job_card.total_completed_qty)
+ & (job_card.status.isin(["Work In Progress", "Material Transferred"]))
+ )
+ ).run()
diff --git a/erpnext/patches/v13_0/update_level_in_bom.py b/erpnext/patches/v13_0/update_level_in_bom.py
index 0d03c42..499412e 100644
--- a/erpnext/patches/v13_0/update_level_in_bom.py
+++ b/erpnext/patches/v13_0/update_level_in_bom.py
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe and Contributors
# 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..e4bc1b3 100644
--- a/erpnext/patches/v13_0/update_member_email_address.py
+++ b/erpnext/patches/v13_0/update_member_email_address.py
@@ -1,10 +1,11 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# 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..e226f1d 100644
--- a/erpnext/patches/v13_0/update_old_loans.py
+++ b/erpnext/patches/v13_0/update_old_loans.py
@@ -1,12 +1,16 @@
-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..aea09ad 100644
--- a/erpnext/patches/v13_0/update_payment_terms_outstanding.py
+++ b/erpnext/patches/v13_0/update_payment_terms_outstanding.py
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# 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..b2e3559 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
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# 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 b41b742..29debc6 100644
--- a/erpnext/patches/v13_0/update_project_template_tasks.py
+++ b/erpnext/patches/v13_0/update_project_template_tasks.py
@@ -1,9 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# 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")
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 ccdc334..f9bfc54 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
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
-from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("hr", "doctype", "employee")
diff --git a/erpnext/patches/v13_0/update_recipient_email_digest.py b/erpnext/patches/v13_0/update_recipient_email_digest.py
index d9aa03f..d4d45af 100644
--- a/erpnext/patches/v13_0/update_recipient_email_digest.py
+++ b/erpnext/patches/v13_0/update_recipient_email_digest.py
@@ -1,9 +1,10 @@
# 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")
diff --git a/erpnext/patches/v13_0/update_response_by_variance.py b/erpnext/patches/v13_0/update_response_by_variance.py
index ef4d976..d65e903 100644
--- a/erpnext/patches/v13_0/update_response_by_variance.py
+++ b/erpnext/patches/v13_0/update_response_by_variance.py
@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe and Contributors
# 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 e642547..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,16 +1,19 @@
# Copyright (c) 2021, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
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)
try:
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..7f61020 100644
--- a/erpnext/patches/v13_0/update_sla_enhancements.py
+++ b/erpnext/patches/v13_0/update_sla_enhancements.py
@@ -1,10 +1,10 @@
# Copyright (c) 2018, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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..665cc39 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
@@ -1,10 +1,10 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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 d25e9c8..b67c74d 100644
--- a/erpnext/patches/v13_0/update_subscription.py
+++ b/erpnext/patches/v13_0/update_subscription.py
@@ -1,9 +1,9 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
import frappe
-from six import iteritems
+
def execute():
@@ -33,7 +33,7 @@
'Based on price list': 'Based On Price List'
}
- for key, value in iteritems(price_determination_map):
+ for key, value in price_determination_map.items():
frappe.db.sql("""
UPDATE `tabSubscription Plan`
SET price_determination = %s
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 d9c3e45..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,5 +1,6 @@
import frappe
+
def execute():
if frappe.db.exists('DocType', 'Member'):
frappe.reload_doc('Non Profit', 'doctype', 'Member')
diff --git a/erpnext/patches/v13_0/update_tds_check_field.py b/erpnext/patches/v13_0/update_tds_check_field.py
index 341b0e8..436d2e6 100644
--- a/erpnext/patches/v13_0/update_tds_check_field.py
+++ b/erpnext/patches/v13_0/update_tds_check_field.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
if frappe.db.has_table("Tax Withholding Category") \
and frappe.db.has_column("Tax Withholding Category", "round_off_tax_amount"):
diff --git a/erpnext/patches/v13_0/update_timesheet_changes.py b/erpnext/patches/v13_0/update_timesheet_changes.py
index a36c84e..a5e3391 100644
--- a/erpnext/patches/v13_0/update_timesheet_changes.py
+++ b/erpnext/patches/v13_0/update_timesheet_changes.py
@@ -1,7 +1,7 @@
-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")
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..902707b
--- /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', force=True)
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }):
+ frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '')
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 7d344f9..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')
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..ad777b8
--- /dev/null
+++ b/erpnext/patches/v13_0/validate_options_for_data_field.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+
+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/patches/v14_0/delete_einvoicing_doctypes.py b/erpnext/patches/v14_0/delete_einvoicing_doctypes.py
index b77d244..a3a8149 100644
--- a/erpnext/patches/v14_0/delete_einvoicing_doctypes.py
+++ b/erpnext/patches/v14_0/delete_einvoicing_doctypes.py
@@ -1,9 +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)
\ No newline at end of file
+ frappe.delete_doc('Custom Field', 'Sales Invoice-irn_cancelled', ignore_missing=True)
diff --git a/erpnext/patches/v14_0/delete_healthcare_doctypes.py b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
new file mode 100644
index 0000000..28fc01b
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
@@ -0,0 +1,49 @@
+import frappe
+
+
+def execute():
+ if "healthcare" in frappe.get_installed_apps():
+ return
+
+ frappe.delete_doc("Workspace", "Healthcare", ignore_missing=True, force=True)
+
+ pages = frappe.get_all("Page", {"module": "healthcare"}, pluck='name')
+ for page in pages:
+ frappe.delete_doc("Page", page, ignore_missing=True, force=True)
+
+ reports = frappe.get_all("Report", {"module": "healthcare", "is_standard": "Yes"}, pluck='name')
+ for report in reports:
+ frappe.delete_doc("Report", report, ignore_missing=True, force=True)
+
+ print_formats = frappe.get_all("Print Format", {"module": "healthcare", "standard": "Yes"}, pluck='name')
+ for print_format in print_formats:
+ frappe.delete_doc("Print Format", print_format, ignore_missing=True, force=True)
+
+ frappe.reload_doc("website", "doctype", "website_settings")
+ forms = frappe.get_all("Web Form", {"module": "healthcare", "is_standard": 1}, pluck='name')
+ for form in forms:
+ frappe.delete_doc("Web Form", form, ignore_missing=True, force=True)
+
+ dashboards = frappe.get_all("Dashboard", {"module": "healthcare", "is_standard": 1}, pluck='name')
+ for dashboard in dashboards:
+ frappe.delete_doc("Dashboard", dashboard, ignore_missing=True, force=True)
+
+ dashboards = frappe.get_all("Dashboard Chart", {"module": "healthcare", "is_standard": 1}, pluck='name')
+ for dashboard in dashboards:
+ frappe.delete_doc("Dashboard Chart", dashboard, ignore_missing=True, force=True)
+
+ frappe.reload_doc("desk", "doctype", "number_card")
+ cards = frappe.get_all("Number Card", {"module": "healthcare", "is_standard": 1}, pluck='name')
+ for card in cards:
+ frappe.delete_doc("Number Card", card, ignore_missing=True, force=True)
+
+ titles = ['Lab Test', 'Prescription', 'Patient Appointment']
+ items = frappe.get_all('Portal Menu Item', filters=[['title', 'in', titles]], pluck='name')
+ for item in items:
+ frappe.delete_doc("Portal Menu Item", item, ignore_missing=True, force=True)
+
+ doctypes = frappe.get_all("DocType", {"module": "healthcare", "custom": 0}, pluck='name')
+ for doctype in doctypes:
+ frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+ frappe.delete_doc("Module Def", "Healthcare", ignore_missing=True, force=True)
diff --git a/erpnext/patches/v14_0/delete_hub_doctypes.py b/erpnext/patches/v14_0/delete_hub_doctypes.py
new file mode 100644
index 0000000..d1e9e31
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_hub_doctypes.py
@@ -0,0 +1,10 @@
+import frappe
+
+
+def execute():
+
+ doctypes = frappe.get_all("DocType", {"module": "Hub Node", "custom": 0}, pluck='name')
+ for doctype in doctypes:
+ frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+ frappe.delete_doc("Module Def", "Hub Node", ignore_missing=True, force=True)
diff --git a/erpnext/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/migrate_crm_settings.py b/erpnext/patches/v14_0/migrate_crm_settings.py
new file mode 100644
index 0000000..30d3ea0
--- /dev/null
+++ b/erpnext/patches/v14_0/migrate_crm_settings.py
@@ -0,0 +1,16 @@
+import frappe
+
+
+def execute():
+ settings = frappe.db.get_value('Selling Settings', 'Selling Settings', [
+ 'campaign_naming_by',
+ 'close_opportunity_after_days',
+ 'default_valid_till'
+ ], as_dict=True)
+
+ frappe.reload_doc('crm', 'doctype', 'crm_settings')
+ frappe.db.set_value('CRM Settings', 'CRM Settings', {
+ 'campaign_naming_by': settings.campaign_naming_by,
+ 'close_opportunity_after_days': settings.close_opportunity_after_days,
+ 'default_valid_till': settings.default_valid_till
+ })
diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py
new file mode 100644
index 0000000..1307147
--- /dev/null
+++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py
@@ -0,0 +1,34 @@
+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..c2ca9be 100644
--- a/erpnext/patches/v4_2/repost_reserved_qty.py
+++ b/erpnext/patches/v4_2/repost_reserved_qty.py
@@ -1,9 +1,11 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
import frappe
-from 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..42b0b04 100644
--- a/erpnext/patches/v4_2/update_requested_and_ordered_qty.py
+++ b/erpnext/patches/v4_2/update_requested_and_ordered_qty.py
@@ -1,11 +1,12 @@
# 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
+
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 +21,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..c46187c 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,6 @@
-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 55f5f82..ed1dffe 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
@@ -1,9 +1,10 @@
# Copyright (c) 2017, Frappe and Contributors
# 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'})
diff --git a/erpnext/patches/v8_1/setup_gst_india.py b/erpnext/patches/v8_1/setup_gst_india.py
index c214990..ff9e6a4 100644
--- a/erpnext/patches/v8_1/setup_gst_india.py
+++ b/erpnext/patches/v8_1/setup_gst_india.py
@@ -1,7 +1,7 @@
-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")
diff --git a/erpnext/patches/v8_7/sync_india_custom_fields.py b/erpnext/patches/v8_7/sync_india_custom_fields.py
index eb24a90..808c833 100644
--- a/erpnext/patches/v8_7/sync_india_custom_fields.py
+++ b/erpnext/patches/v8_7/sync_india_custom_fields.py
@@ -1,6 +1,7 @@
-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 381f399..bf8bd05 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -1,22 +1,22 @@
-# -*- 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
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 +95,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,27 +123,28 @@
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
return amount_per_day * no_of_days
+@frappe.whitelist()
def get_additional_salaries(employee, start_date, end_date, component_type):
- additional_salary_list = frappe.db.sql("""
- select name, salary_component as component, type, amount,
- overwrite_salary_structure_amount as overwrite,
- deduct_full_tax_on_selected_payroll_date
- from `tabAdditional Salary`
- where employee=%(employee)s
- and docstatus = 1
- and (
- payroll_date between %(from_date)s and %(to_date)s
- or
- from_date <= %(to_date)s and to_date >= %(to_date)s
- )
- and type = %(component_type)s
- order by salary_component, overwrite ASC
- """, {
- 'employee': employee,
- 'from_date': start_date,
- 'to_date': end_date,
- 'component_type': "Earning" if component_type == "earnings" else "Deduction"
- }, as_dict=1)
+ comp_type = 'Earning' if component_type == 'earnings' else 'Deduction'
+
+ additional_sal = frappe.qb.DocType('Additional Salary')
+ component_field = additional_sal.salary_component.as_('component')
+ overwrite_field = additional_sal.overwrite_salary_structure_amount.as_('overwrite')
+
+ additional_salary_list = frappe.qb.from_(
+ additional_sal
+ ).select(
+ additional_sal.name, component_field, additional_sal.type,
+ additional_sal.amount, additional_sal.is_recurring, overwrite_field,
+ additional_sal.deduct_full_tax_on_selected_payroll_date
+ ).where(
+ (additional_sal.employee == employee)
+ & (additional_sal.docstatus == 1)
+ & (additional_sal.type == comp_type)
+ ).where(
+ additional_sal.payroll_date[start_date: end_date]
+ | ((additional_sal.from_date <= end_date) & (additional_sal.to_date >= end_date))
+ ).run(as_dict=True)
additional_salaries = []
components_to_overwrite = []
diff --git a/erpnext/payroll/doctype/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 2a9c561..84de912 100644
--- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
@@ -1,13 +1,18 @@
-# -*- coding: utf-8 -*-
# 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
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 a1cde08..eda5015 100644
--- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
@@ -1,15 +1,26 @@
-# -*- 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.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_holiday_dates_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):
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..02149ad 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
@@ -1,8 +1,8 @@
-# -*- coding: utf-8 -*-
# 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..51aa2c9 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..801ce4b 100644
--- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
@@ -1,16 +1,21 @@
-# -*- 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.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..b1d3c66 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
@@ -1,8 +1,8 @@
-# -*- coding: utf-8 -*-
# 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..a37e224 100644
--- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
@@ -1,13 +1,14 @@
-# -*- 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 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..e296fdf 100644
--- a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py
+++ b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..51059a1 100644
--- a/erpnext/payroll/doctype/employee_other_income/employee_other_income.py
+++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..8f0f637 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
@@ -1,10 +1,9 @@
-# -*- 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 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..5c109de 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
@@ -1,10 +1,9 @@
-# -*- 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 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..84e6183 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..9b5eab6 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
@@ -1,15 +1,20 @@
-# -*- 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
-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..fc28afd 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
@@ -1,13 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# 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..4322f31 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..56e73b3 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
@@ -1,14 +1,18 @@
-# -*- 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
-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..f2aa64c 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
@@ -1,11 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_exemption_category, create_payroll_period
+
+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..37209e5 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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 d3f24c9..4ac11f7 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
@@ -1,12 +1,12 @@
-# -*- 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.utils import flt
from frappe.model.document import Document
+from frappe.utils import flt
+
class EmployeeTaxExemptionSubCategory(Document):
def validate(self):
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..64d2e3a 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeTaxExemptionSubCategory(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.js b/erpnext/payroll/doctype/gratuity/gratuity.js
index 377f3c6..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',
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 8cb804d..476990a 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/gratuity.py
@@ -1,14 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# 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 +21,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 +64,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
@@ -207,7 +193,7 @@
sal_slip = get_last_salary_slip(employee)
if not sal_slip:
frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee)))
- component_and_amounts = frappe.get_list("Salary Detail",
+ component_and_amounts = frappe.get_all("Salary Detail",
filters={
"docstatus": 1,
'parent': sal_slip,
@@ -242,7 +228,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 483e346..aeadba1 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
+++ b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'reference_name',
@@ -11,10 +11,6 @@
{
'label': _('Payment'),
'items': ['Payment Entry']
- },
- {
- 'label': _('Additional Salary'),
- 'items': ['Additional Salary']
}
]
}
diff --git a/erpnext/payroll/doctype/gratuity/test_gratuity.py b/erpnext/payroll/doctype/gratuity/test_gratuity.py
index 7daea2d..93cba06 100644
--- a/erpnext/payroll/doctype/gratuity/test_gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/test_gratuity.py
@@ -1,17 +1,20 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+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 +25,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'])
@@ -41,7 +48,7 @@
self.assertEqual(floor(experience), gratuity.current_work_experience)
#amount Calculation
- component_amount = frappe.get_list("Salary Detail",
+ component_amount = frappe.get_all("Salary Detail",
filters={
"docstatus": 1,
'parent': sal_slip,
@@ -57,9 +64,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)")
@@ -80,7 +84,7 @@
self.assertEqual(floor(experience), gratuity.current_work_experience)
#amount Calculation
- component_amount = frappe.get_list("Salary Detail",
+ component_amount = frappe.get_all("Salary Detail",
filters={
"docstatus": 1,
'parent': sal_slip,
@@ -137,14 +141,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..9c1657d 100644
--- a/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py
+++ b/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class GratuityApplicableComponent(Document):
pass
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
index 29a6ebe..d30cfc6 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
@@ -1,18 +1,18 @@
-# -*- 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 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 0f27315..e7c67fb 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'gratuity_rule',
diff --git a/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
index 1f5dc4e..8393050 100644
--- a/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
+++ b/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
@@ -1,10 +1,9 @@
-# -*- 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 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..2ae6b54 100644
--- a/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py
+++ b/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..040b2c8 100644
--- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
@@ -1,11 +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
+
+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..680cb3b 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
@@ -1,10 +1,9 @@
-# -*- 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 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..53911a9 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..8cc426b 100644
--- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py
+++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py
@@ -1,9 +1,9 @@
-# -*- 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 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..84c59a2 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -1,17 +1,30 @@
-# -*- 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, 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 +542,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 0346a7c..a33b28d 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'payroll_entry',
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index b80b320..c6f3897 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -1,19 +1,36 @@
# 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 66dec07..659ec6d 100644
--- a/erpnext/payroll/doctype/payroll_period/payroll_period.py
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py
@@ -1,14 +1,15 @@
-# -*- 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.utils import date_diff, getdate, formatdate, cint, month_diff, flt, add_months
from frappe.model.document import Document
+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):
self.validate_dates()
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
index e332995..8a3332f 100644
--- a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'payroll_period',
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..61967c0 100644
--- a/erpnext/payroll/doctype/payroll_period/test_payroll_period.py
+++ b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..c90a76a 100644
--- a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py
+++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..6fd3094 100644
--- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
@@ -1,13 +1,13 @@
-# -*- 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.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..3b96db6 100644
--- a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py
+++ b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py
@@ -1,10 +1,9 @@
-# -*- 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 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..10e8381 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
@@ -1,13 +1,15 @@
-# -*- 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
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..c86bf33 100644
--- a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestRetentionBonus(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.py b/erpnext/payroll/doctype/salary_component/salary_component.py
index 7c92631..b8def58 100644
--- a/erpnext/payroll/doctype/salary_component/salary_component.py
+++ b/erpnext/payroll/doctype/salary_component/salary_component.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, 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.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..6e00971 100644
--- a/erpnext/payroll/doctype/salary_component/test_salary_component.py
+++ b/erpnext/payroll/doctype/salary_component/test_salary_component.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
# test_records = frappe.get_test_records('Salary Component')
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json
index 393f647..665f0a8 100644
--- a/erpnext/payroll/doctype/salary_detail/salary_detail.json
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json
@@ -12,6 +12,7 @@
"year_to_date",
"section_break_5",
"additional_salary",
+ "is_recurring_additional_salary",
"statistical_component",
"depends_on_payment_days",
"exempted_from_income_tax",
@@ -235,11 +236,19 @@
"label": "Year To Date",
"options": "currency",
"read_only": 1
- }
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.additional_salary",
+ "fieldname": "is_recurring_additional_salary",
+ "fieldtype": "Check",
+ "label": "Is Recurring Additional Salary",
+ "read_only": 1
+ }
],
"istable": 1,
"links": [],
- "modified": "2021-01-14 13:39:15.847158",
+ "modified": "2021-08-30 13:39:15.847158",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Detail",
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.py b/erpnext/payroll/doctype/salary_detail/salary_detail.py
index 0b18754..c74bd54 100644
--- a/erpnext/payroll/doctype/salary_detail/salary_detail.py
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class SalaryDetail(Document):
pass
diff --git a/erpnext/payroll/doctype/salary_slip/__init__.py b/erpnext/payroll/doctype/salary_slip/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/payroll/doctype/salary_slip/__init__.py
+++ b/erpnext/payroll/doctype/salary_slip/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 6325351..05af09e 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -1,27 +1,49 @@
# 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, 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 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.hr.utils import get_holiday_dates_for_employee
-from erpnext.utilities.transaction_base import TransactionBase
+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 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
+
+import erpnext
from erpnext.accounts.utils import get_fiscal_year
-from erpnext.hr.utils import validate_active_employee
-from six import iteritems
+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):
@@ -148,7 +170,6 @@
and employee = %s and name != %s {0}""".format(cond),
(self.start_date, self.end_date, self.employee, self.name))
if ret_exist:
- self.employee = ''
frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
else:
for data in self.timesheets:
@@ -463,7 +484,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):
@@ -606,7 +627,8 @@
get_salary_component_data(additional_salary.component),
additional_salary.amount,
component_type,
- additional_salary
+ additional_salary,
+ is_recurring = additional_salary.is_recurring
)
def add_tax_components(self, payroll_period):
@@ -627,7 +649,7 @@
tax_row = get_salary_component_data(d)
self.update_component_row(tax_row, tax_amount, "deductions")
- def update_component_row(self, component_data, amount, component_type, additional_salary=None):
+ def update_component_row(self, component_data, amount, component_type, additional_salary=None, is_recurring = 0):
component_row = None
for d in self.get(component_type):
if d.salary_component != component_data.salary_component:
@@ -674,6 +696,8 @@
else:
component_row.default_amount = 0
component_row.additional_amount = amount
+
+ component_row.is_recurring_additional_salary = is_recurring
component_row.additional_salary = additional_salary.name
component_row.deduct_full_tax_on_selected_payroll_date = \
additional_salary.deduct_full_tax_on_selected_payroll_date
@@ -685,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}.")
@@ -842,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
@@ -860,28 +888,39 @@
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:
- taxable_earnings += (amount - additional_amount)
- additional_income += additional_amount
- if earning.deduct_full_tax_on_selected_payroll_date:
- additional_income_with_full_tax += additional_amount
- continue
-
if earning.is_flexible_benefit:
flexi_benefits += amount
else:
- taxable_earnings += amount
+ taxable_earnings += (amount - additional_amount)
+ additional_income += additional_amount
+
+ # Get additional amount based on future recurring additional salary
+ if additional_amount and earning.is_recurring_additional_salary:
+ additional_income += self.get_future_recurring_additional_amount(earning.additional_salary,
+ earning.additional_amount) # Used earning.additional_amount to consider the amount for the full month
+
+ if earning.deduct_full_tax_on_selected_payroll_date:
+ additional_income_with_full_tax += additional_amount
if allow_tax_exemption:
for ded in self.deductions:
if ded.exempted_from_income_tax:
- amount = ded.amount
+ amount, additional_amount = ded.amount, ded.additional_amount
if based_on_payment_days:
- amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0]
- taxable_earnings -= flt(amount)
+ amount, additional_amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)
+
+ taxable_earnings -= flt(amount - additional_amount)
+ additional_income -= additional_amount
+
+ if additional_amount and ded.is_recurring_additional_salary:
+ additional_income -= self.get_future_recurring_additional_amount(ded.additional_salary,
+ ded.additional_amount) # Used ded.additional_amount to consider the amount for the full month
return frappe._dict({
"taxable_earnings": taxable_earnings,
@@ -890,11 +929,21 @@
"flexi_benefits": flexi_benefits
})
+ def get_future_recurring_additional_amount(self, additional_salary, monthly_additional_amount):
+ future_recurring_additional_amount = 0
+ to_date = frappe.db.get_value("Additional Salary", additional_salary, 'to_date')
+ # future month count excluding current
+ future_recurring_period = (getdate(to_date).month - getdate(self.start_date).month)
+ if future_recurring_period > 0:
+ future_recurring_additional_amount = monthly_additional_amount * future_recurring_period # Used earning.additional_amount to consider the amount for the full month
+ return future_recurring_additional_amount
+
def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
amount, additional_amount = row.amount, row.additional_amount
if (self.salary_structure and
- cint(row.depends_on_payment_days) and cint(self.total_working_days) and
- (not self.salary_slip_based_on_timesheet or
+ cint(row.depends_on_payment_days) and cint(self.total_working_days)
+ and not (row.additional_salary and row.default_amount) # to identify overwritten additional salary
+ and (not self.salary_slip_based_on_timesheet or
getdate(self.start_date) < joining_date or
(relieving_date and getdate(self.end_date) > relieving_date)
)):
@@ -1031,7 +1080,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"])
@@ -1041,9 +1090,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
@@ -1214,25 +1261,28 @@
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'],
- filters = {'employee_name' : self.employee_name,
+ fields = ['sum(net_pay) as net_sum', 'sum(gross_pay) as gross_sum'],
+ filters = {'employee' : self.employee,
'start_date' : ['>=', period_start_date],
'end_date' : ['<', period_end_date],
'name': ['!=', self.name],
'docstatus': 1
})
- year_to_date = flt(salary_slip_sum[0].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
first_day_of_the_month = get_first_day(self.start_date)
salary_slip_sum = frappe.get_list('Salary Slip',
fields = ['sum(net_pay) as sum'],
- filters = {'employee_name' : self.employee_name,
+ filters = {'employee' : self.employee,
'start_date' : ['>=', first_day_of_the_month],
'end_date' : ['<', self.start_date],
'name': ['!=', self.name],
@@ -1256,13 +1306,13 @@
INNER JOIN `tabSalary Slip` as salary_slip
ON detail.parent = salary_slip.name
WHERE
- salary_slip.employee_name = %(employee_name)s
+ salary_slip.employee = %(employee)s
AND detail.salary_component = %(component)s
AND salary_slip.start_date >= %(period_start_date)s
AND salary_slip.end_date < %(period_end_date)s
AND salary_slip.name != %(docname)s
AND salary_slip.docstatus = 1""",
- {'employee_name': self.employee_name, 'component': component.salary_component, 'period_start_date': period_start_date,
+ {'employee': self.employee, 'component': component.salary_component, 'period_start_date': period_start_date,
'period_end_date': period_end_date, 'docname': self.name}
)
@@ -1291,7 +1341,7 @@
from erpnext.hr.doctype.leave_application.leave_application import get_leave_details
leave_details = get_leave_details(self.employee, self.end_date)
- for leave_type, leave_values in iteritems(leave_details['leave_allocation']):
+ for leave_type, leave_values in leave_details['leave_allocation'].items():
self.append('leave_details', {
'leave_type': leave_type,
'total_allocated_leaves': flt(leave_values.get('total_leaves')),
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index d730fcf..e4618c3 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -1,21 +1,35 @@
# 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 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 +134,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 +221,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 +300,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 +462,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,
@@ -460,6 +535,61 @@
# undelete fixture data
frappe.db.rollback()
+ def test_tax_for_recurring_additional_salary(self):
+ frappe.db.sql("""delete from `tabPayroll Period`""")
+ frappe.db.sql("""delete from `tabSalary Component`""")
+
+ payroll_period = create_payroll_period()
+
+ create_tax_slab(payroll_period, allow_tax_exemption=True)
+
+ employee = make_employee("test_tax@salary.slip")
+ delete_docs = [
+ "Salary Slip",
+ "Additional Salary",
+ "Employee Tax Exemption Declaration",
+ "Employee Tax Exemption Proof Submission",
+ "Employee Benefit Claim",
+ "Salary Structure Assignment"
+ ]
+ for doc in delete_docs:
+ frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee))
+
+ from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+ salary_structure = make_salary_structure("Stucture to test tax", "Monthly",
+ other_details={"max_benefits": 100000}, test_tax=True,
+ employee=employee, payroll_period=payroll_period)
+
+
+ create_salary_slips_for_payroll_period(employee, salary_structure.name,
+ payroll_period, deduct_random=False, num=3)
+
+ tax_paid = get_tax_paid_in_period(employee)
+
+ annual_tax = 23196.0
+ self.assertEqual(tax_paid, annual_tax)
+
+ frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
+
+ #------------------------------------
+ # Recurring additional salary
+ start_date = add_months(payroll_period.start_date, 3)
+ end_date = add_months(payroll_period.start_date, 5)
+ create_recurring_additional_salary(employee, "Performance Bonus", 20000, start_date, end_date)
+
+ frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
+
+ create_salary_slips_for_payroll_period(employee, salary_structure.name,
+ payroll_period, deduct_random=False, num=4)
+
+ tax_paid = get_tax_paid_in_period(employee)
+
+ annual_tax = 32315.0
+ self.assertEqual(tax_paid, annual_tax)
+
+ frappe.db.rollback()
+
def make_activity_for_employee(self):
activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
activity_type.billing_rate = 50
@@ -828,7 +958,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 +969,109 @@
}).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
+
+def create_recurring_additional_salary(employee, salary_component, amount, from_date, to_date, company=None):
+ frappe.get_doc({
+ "doctype": "Additional Salary",
+ "employee": employee,
+ "company": company or erpnext.get_default_company(),
+ "salary_component": salary_component,
+ "is_recurring": 1,
+ "from_date": from_date,
+ "to_date": to_date,
+ "amount": amount,
+ "type": "Earning",
+ "currency": erpnext.get_default_currency()
+ }).submit()
diff --git a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
index 7a92bf1..b29a60b 100644
--- a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
+++ b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
@@ -1,10 +1,10 @@
-# -*- 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 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..022eba0 100644
--- a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py
+++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class SalarySlipTimesheet(Document):
pass
diff --git a/erpnext/payroll/doctype/salary_structure/__init__.py b/erpnext/payroll/doctype/salary_structure/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/payroll/doctype/salary_structure/__init__.py
+++ b/erpnext/payroll/doctype/salary_structure/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py
index 6dfb3a5..ae83c04 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py
@@ -1,14 +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, 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):
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
index 0159e35..014d0ba 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'salary_structure',
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index 3957d83..e2d0d1c 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -1,18 +1,24 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-import erpnext
+from frappe.utils import add_years, date_diff, get_first_day, nowdate
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 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 5fb3ce2..e1ff9ca 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -1,12 +1,12 @@
-# -*- 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.utils import getdate
from frappe.model.document import Document
+from frappe.utils import getdate
+
class DuplicateAssignment(frappe.ValidationError): pass
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..56dd0d0 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..d1ccbe3 100644
--- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py
+++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class TaxableSalarySlab(Document):
pass
diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.py b/erpnext/payroll/notification/retention_bonus/retention_bonus.py
index 2334f8b..02e3e93 100644
--- a/erpnext/payroll/notification/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py
index 05a5366..6c3bd37 100644
--- a/erpnext/payroll/report/bank_remittance/bank_remittance.py
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py
@@ -1,12 +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.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.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
index 8a79416..75a9f97 100644
--- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
@@ -1,10 +1,13 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
index a0dab63..fa68575 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
@@ -1,10 +1,15 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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..578c816 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
@@ -1,10 +1,13 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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..78deb22 100644
--- a/erpnext/payroll/report/salary_register/salary_register.py
+++ b/erpnext/payroll/report/salary_register/salary_register.py
@@ -1,10 +1,13 @@
# 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, 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 = {}
@@ -131,11 +134,11 @@
ss_earning_map = {}
for d in ss_earnings:
- ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
+ ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0)
if currency == company_currency:
- ss_earning_map[d.parent][d.salary_component] = flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1)
+ ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1)
else:
- ss_earning_map[d.parent][d.salary_component] = flt(d.amount)
+ ss_earning_map[d.parent][d.salary_component] += flt(d.amount)
return ss_earning_map
@@ -146,10 +149,10 @@
ss_ded_map = {}
for d in ss_deductions:
- ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
+ ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0)
if currency == company_currency:
- ss_ded_map[d.parent][d.salary_component] = flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1)
+ ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1)
else:
- ss_ded_map[d.parent][d.salary_component] = flt(d.amount)
+ ss_ded_map[d.parent][d.salary_component] += flt(d.amount)
return ss_ded_map
diff --git a/erpnext/payroll/workspace/payroll/payroll.json b/erpnext/payroll/workspace/payroll/payroll.json
index b55bdc7..7246dae 100644
--- a/erpnext/payroll/workspace/payroll/payroll.json
+++ b/erpnext/payroll/workspace/payroll/payroll.json
@@ -1,5 +1,4 @@
{
- "category": "",
"charts": [
{
"chart_name": "Outgoing Salary",
@@ -8,18 +7,12 @@
],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Payroll\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Structure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payroll Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Slip\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Income Tax Slab\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payroll\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Compensations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-05-27 19:54:23.405607",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "money-coins-1",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Payroll",
"links": [
{
@@ -319,15 +312,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:01.335324",
+ "modified": "2021-08-05 12:16:01.335325",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll",
- "onboarding": "Payroll",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py
index 54ea7c6..1e056a6 100644
--- a/erpnext/portal/doctype/homepage/homepage.py
+++ b/erpnext/portal/doctype/homepage/homepage.py
@@ -1,12 +1,12 @@
-# -*- 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.model.document import Document
from frappe.website.utils import delete_page_cache
+
class Homepage(Document):
def validate(self):
if not self.description:
diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py
index e646775..fb0367f 100644
--- a/erpnext/portal/doctype/homepage/test_homepage.py
+++ b/erpnext/portal/doctype/homepage/test_homepage.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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..c21461d 100644
--- a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py
+++ b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py
@@ -1,10 +1,9 @@
-# -*- 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.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..7181aff 100644
--- a/erpnext/portal/doctype/homepage_section/homepage_section.py
+++ b/erpnext/portal/doctype/homepage_section/homepage_section.py
@@ -1,11 +1,11 @@
-# -*- 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
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..b30d983 100644
--- a/erpnext/portal/doctype/homepage_section/test_homepage_section.py
+++ b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
@@ -1,14 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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..eeff63c 100644
--- a/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py
+++ b/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py
@@ -1,9 +1,9 @@
-# -*- 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 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..0e106c6 100644
--- a/erpnext/portal/doctype/products_settings/products_settings.py
+++ b/erpnext/portal/doctype/products_settings/products_settings.py
@@ -1,12 +1,12 @@
-# -*- 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.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..66026fc 100644
--- a/erpnext/portal/doctype/products_settings/test_products_settings.py
+++ b/erpnext/portal/doctype/products_settings/test_products_settings.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..58a7376 100644
--- a/erpnext/portal/doctype/website_attribute/website_attribute.py
+++ b/erpnext/portal/doctype/website_attribute/website_attribute.py
@@ -1,9 +1,9 @@
-# -*- 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 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..8067ebb 100644
--- a/erpnext/portal/doctype/website_filter_field/website_filter_field.py
+++ b/erpnext/portal/doctype/website_filter_field/website_filter_field.py
@@ -1,9 +1,9 @@
-# -*- 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 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 ec7c83a..b478489 100644
--- a/erpnext/portal/product_configurator/test_product_configurator.py
+++ b/erpnext/portal/product_configurator/test_product_configurator.py
@@ -1,8 +1,9 @@
-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"]
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..974b51e 100644
--- a/erpnext/portal/utils.py
+++ b/erpnext/portal/utils.py
@@ -1,9 +1,12 @@
-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/__init__.py b/erpnext/projects/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/projects/doctype/__init__.py
+++ b/erpnext/projects/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.py b/erpnext/projects/doctype/activity_cost/activity_cost.py
index 99226ea..bc4bb9d 100644
--- a/erpnext/projects/doctype/activity_cost/activity_cost.py
+++ b/erpnext/projects/doctype/activity_cost/activity_cost.py
@@ -1,12 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class DuplicationError(frappe.ValidationError): pass
class ActivityCost(Document):
diff --git a/erpnext/projects/doctype/activity_cost/test_activity_cost.py b/erpnext/projects/doctype/activity_cost/test_activity_cost.py
index 5f35f29..d53e582 100644
--- a/erpnext/projects/doctype/activity_cost/test_activity_cost.py
+++ b/erpnext/projects/doctype/activity_cost/test_activity_cost.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# 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`")
diff --git a/erpnext/projects/doctype/activity_type/activity_type.py b/erpnext/projects/doctype/activity_type/activity_type.py
index 50e18ef..5151098 100644
--- a/erpnext/projects/doctype/activity_type/activity_type.py
+++ b/erpnext/projects/doctype/activity_type/activity_type.py
@@ -1,8 +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
+
from frappe.model.document import Document
+
class ActivityType(Document):
pass
diff --git a/erpnext/projects/doctype/activity_type/test_activity_type.py b/erpnext/projects/doctype/activity_type/test_activity_type.py
index dcb0101..bb74b88 100644
--- a/erpnext/projects/doctype/activity_type/test_activity_type.py
+++ b/erpnext/projects/doctype/activity_type/test_activity_type.py
@@ -1,7 +1,6 @@
# 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
+
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..73ce8f9 100644
--- a/erpnext/projects/doctype/dependent_task/dependent_task.py
+++ b/erpnext/projects/doctype/dependent_task/dependent_task.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class DependentTask(Document):
pass
diff --git a/erpnext/projects/doctype/project/__init__.py b/erpnext/projects/doctype/project/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/projects/doctype/project/__init__.py
+++ b/erpnext/projects/doctype/project/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 1e4b2b0..6281c1a 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -1,20 +1,20 @@
# Copyright (c) 2017, 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 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 +143,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.py b/erpnext/projects/doctype/project/project_dashboard.py
index 39cf016..a7d1835 100644
--- a/erpnext/projects/doctype/project/project_dashboard.py
+++ b/erpnext/projects/doctype/project/project_dashboard.py
@@ -1,6 +1,6 @@
-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..df42e82 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.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 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 +97,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 2426fd2..3cc8d68 100644
--- a/erpnext/projects/doctype/project_template/project_template.py
+++ b/erpnext/projects/doctype/project_template/project_template.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 import _
+from frappe.model.document import Document
from frappe.utils import get_link_to_form
+
class ProjectTemplate(Document):
def validate(self):
diff --git a/erpnext/projects/doctype/project_template/project_template_dashboard.py b/erpnext/projects/doctype/project_template/project_template_dashboard.py
index 67f74f5..a03a57d 100644
--- a/erpnext/projects/doctype/project_template/project_template_dashboard.py
+++ b/erpnext/projects/doctype/project_template/project_template_dashboard.py
@@ -1,5 +1,3 @@
-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 d546fd0..8424833 100644
--- a/erpnext/projects/doctype/project_template/test_project_template.py
+++ b/erpnext/projects/doctype/project_template/test_project_template.py
@@ -1,12 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
from erpnext.projects.doctype.task.test_task import create_task
+
class TestProjectTemplate(unittest.TestCase):
pass
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..01ec935 100644
--- a/erpnext/projects/doctype/project_template_task/project_template_task.py
+++ b/erpnext/projects/doctype/project_template_task/project_template_task.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 ProjectTemplateTask(Document):
pass
diff --git a/erpnext/projects/doctype/project_type/project_type.py b/erpnext/projects/doctype/project_type/project_type.py
index 36137ca..4a3724d 100644
--- a/erpnext/projects/doctype/project_type/project_type.py
+++ b/erpnext/projects/doctype/project_type/project_type.py
@@ -1,11 +1,11 @@
-# -*- 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
+
import frappe
from frappe import _
+from frappe.model.document import Document
+
class ProjectType(Document):
def on_trash(self):
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..3e670d0 100644
--- a/erpnext/projects/doctype/project_type/test_project_type.py
+++ b/erpnext/projects/doctype/project_type/test_project_type.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 2e1ec74..42ba5f6 100644
--- a/erpnext/projects/doctype/project_update/project_update.py
+++ b/erpnext/projects/doctype/project_update/project_update.py
@@ -1,11 +1,11 @@
-# -*- 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 ProjectUpdate(Document):
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 2edd2f8..f29c931 100644
--- a/erpnext/projects/doctype/project_update/test_project_update.py
+++ b/erpnext/projects/doctype/project_update/test_project_update.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestProjectUpdate(unittest.TestCase):
pass
diff --git a/erpnext/projects/doctype/project_user/project_user.py b/erpnext/projects/doctype/project_user/project_user.py
index 3198f3b..a52bcb1 100644
--- a/erpnext/projects/doctype/project_user/project_user.py
+++ b/erpnext/projects/doctype/project_user/project_user.py
@@ -1,10 +1,9 @@
-# -*- 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.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..db1dc45 100644
--- a/erpnext/projects/doctype/projects_settings/projects_settings.py
+++ b/erpnext/projects/doctype/projects_settings/projects_settings.py
@@ -1,10 +1,9 @@
-# -*- 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 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..79e7832 100644
--- a/erpnext/projects/doctype/projects_settings/test_projects_settings.py
+++ b/erpnext/projects/doctype/projects_settings/test_projects_settings.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestProjectsSettings(unittest.TestCase):
pass
diff --git a/erpnext/projects/doctype/task/__init__.py b/erpnext/projects/doctype/task/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/projects/doctype/task/__init__.py
+++ b/erpnext/projects/doctype/task/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 5976e01..9b1ea04 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -1,7 +1,6 @@
# 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
@@ -9,7 +8,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_dashboard.py b/erpnext/projects/doctype/task/task_dashboard.py
index b776b98..f7470f8 100644
--- a/erpnext/projects/doctype/task/task_dashboard.py
+++ b/erpnext/projects/doctype/task/task_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py
index 0fad5e8..a0ac7c1 100644
--- a/erpnext/projects/doctype/task/test_task.py
+++ b/erpnext/projects/doctype/task/test_task.py
@@ -1,12 +1,14 @@
# 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..0db1f81 100644
--- a/erpnext/projects/doctype/task_depends_on/task_depends_on.py
+++ b/erpnext/projects/doctype/task_depends_on/task_depends_on.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..08bed69 100644
--- a/erpnext/projects/doctype/task_type/task_type.py
+++ b/erpnext/projects/doctype/task_type/task_type.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..ef99402 100644
--- a/erpnext/projects/doctype/task_type/test_task_type.py
+++ b/erpnext/projects/doctype/task_type/test_task_type.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# 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..d59cc01 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -1,23 +1,28 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import datetime
+import unittest
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
+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.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 1655b76..f615f05 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -32,12 +32,12 @@
};
},
- onload: function(frm){
+ onload: function(frm) {
if (frm.doc.__islocal && frm.doc.time_logs) {
calculate_time_and_amount(frm);
}
- if (frm.is_new()) {
+ if (frm.is_new() && !frm.doc.employee) {
set_employee_and_company(frm);
}
},
@@ -283,7 +283,9 @@
calculate_time_and_amount(frm);
},
- activity_type: function(frm, cdt, cdn) {
+ activity_type: function (frm, cdt, cdn) {
+ if (!frappe.get_doc(cdt, cdn).activity_type) return;
+
frappe.call({
method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost",
args: {
@@ -291,10 +293,10 @@
activity_type: frm.selected_doc.activity_type,
currency: frm.doc.currency
},
- callback: function(r){
- if(r.message){
- frappe.model.set_value(cdt, cdn, 'billing_rate', r.message['billing_rate']);
- frappe.model.set_value(cdt, cdn, 'costing_rate', r.message['costing_rate']);
+ callback: function (r) {
+ if (r.message) {
+ frappe.model.set_value(cdt, cdn, "billing_rate", r.message["billing_rate"]);
+ frappe.model.set_value(cdt, cdn, "costing_rate", r.message["costing_rate"]);
calculate_billing_costing_amount(frm, cdt, cdn);
}
}
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 5f569d6..e92785e 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -1,21 +1,18 @@
-# -*- 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 _
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,25 +213,47 @@
@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,
- 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 {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):
diff --git a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
index 088d98c..15fe797 100644
--- a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
+++ b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'time_sheet',
diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py
index 7da94b7..d527a3c 100644
--- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py
+++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py
@@ -1,10 +1,9 @@
-# -*- 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.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 a22ed7b..46479d0 100644
--- a/erpnext/projects/report/billing_summary.py
+++ b/erpnext/projects/report/billing_summary.py
@@ -2,10 +2,11 @@
# 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, flt
+from frappe.utils import flt, time_diff_in_hours
+
def get_columns():
return [
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 3dcae5b..f733768 100644
--- a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py
+++ b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py
@@ -1,11 +1,12 @@
# 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.desk.reportview import build_match_conditions
+
def execute(filters=None):
if not filters:
filters = {}
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..3ab2bb6 100644
--- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
+++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
@@ -1,10 +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.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 78291b2..e2ba7c2 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,10 +1,12 @@
-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):
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 17c92c2..a2f7378 100644
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
+++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
@@ -1,11 +1,12 @@
# 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 erpnext.projects.report.billing_summary import get_columns, get_data
+
def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns()
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..dd4f8ea 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
@@ -1,11 +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 _
from frappe.utils import flt, getdate
-from six import iteritems
+
def execute(filters=None):
return EmployeeHoursReport(filters).run()
@@ -109,7 +109,7 @@
self.data = []
- for emp, data in iteritems(self.stats_by_employee):
+ for emp, data in self.stats_by_employee.items():
row = frappe._dict()
row['employee'] = emp
row.update(data)
@@ -179,7 +179,7 @@
def calculate_utilizations(self):
TOTAL_HOURS = flt(self.standard_working_hours * self.day_span, 2)
- for emp, data in iteritems(self.stats_by_employee):
+ for emp, data in self.stats_by_employee.items():
data['total_hours'] = TOTAL_HOURS
data['untracked_hours'] = flt(TOTAL_HOURS - data['billed_hours'] - data['non_billed_hours'], 2)
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 969fc55..b500bc8 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,14 @@
-from __future__ import unicode_literals
import unittest
-import frappe
+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
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 17c92c2..a2f7378 100644
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.py
+++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.py
@@ -1,11 +1,12 @@
# 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 erpnext.projects.report.billing_summary import get_columns, get_data
+
def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns()
diff --git a/erpnext/projects/report/project_profitability/project_profitability.py b/erpnext/projects/report/project_profitability/project_profitability.py
index 0a52f7b..9520cd1 100644
--- a/erpnext/projects/report/project_profitability/project_profitability.py
+++ b/erpnext/projects/report/project_profitability/project_profitability.py
@@ -1,11 +1,12 @@
# 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 flt
+
def execute(filters=None):
columns, data = [], []
data = get_data(filters)
diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index 180926f..0415690 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -1,28 +1,38 @@
-from __future__ import unicode_literals
import unittest
+
import frappe
-from frappe.utils import getdate, nowdate, add_days
+from frappe.utils import add_days, getdate
+
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):
+class TestProjectProfitability(unittest.TestCase):
def setUp(self):
+ frappe.db.sql('delete from `tabTimesheet`')
emp = make_employee('test_employee_9@salary.com', company='_Test Company')
+
if not frappe.db.exists('Salary Component', 'Timesheet Component'):
frappe.get_doc({'doctype': 'Salary Component', 'salary_component': 'Timesheet Component'}).insert()
+
make_salary_structure_for_timesheet(emp, company='_Test Company')
- self.timesheet = make_timesheet(emp, simulate = True, is_billable=1)
+ date = getdate()
+
+ self.timesheet = make_timesheet(emp, is_billable=1)
self.salary_slip = make_salary_slip(self.timesheet.name)
- holidays = self.salary_slip.get_holidays_for_employee(nowdate(), nowdate())
+
+ holidays = self.salary_slip.get_holidays_for_employee(date, date)
if holidays:
frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 1)
self.salary_slip.submit()
self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer')
- self.sales_invoice.due_date = nowdate()
+ self.sales_invoice.due_date = date
self.sales_invoice.submit()
frappe.db.set_value('HR Settings', None, 'standard_working_hours', 8)
@@ -58,6 +68,4 @@
self.assertEqual(fractional_cost, row.fractional_cost)
def tearDown(self):
- frappe.get_doc("Sales Invoice", self.sales_invoice.name).cancel()
- frappe.get_doc("Salary Slip", self.salary_slip.name).cancel()
- frappe.get_doc("Timesheet", self.timesheet.name).cancel()
+ frappe.db.rollback()
diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py
index 98dd617..ce1b701 100644
--- a/erpnext/projects/report/project_summary/project_summary.py
+++ b/erpnext/projects/report/project_summary/project_summary.py
@@ -1,10 +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 = 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..31bcc3b 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
@@ -1,10 +1,10 @@
# 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 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..5d74550 100644
--- a/erpnext/projects/utils.py
+++ b/erpnext/projects/utils.py
@@ -3,9 +3,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.py b/erpnext/projects/web_form/tasks/tasks.py
index e5a9404..99249ed 100644
--- a/erpnext/projects/web_form/tasks/tasks.py
+++ b/erpnext/projects/web_form/tasks/tasks.py
@@ -1,7 +1,6 @@
-from __future__ import unicode_literals
-
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}]
diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json
index 065f1ed..1df2b08 100644
--- a/erpnext/projects/workspace/projects/projects.json
+++ b/erpnext/projects/workspace/projects/projects.json
@@ -1,5 +1,4 @@
{
- "category": "",
"charts": [
{
"chart_name": "Project Summary",
@@ -8,18 +7,12 @@
],
"content": "[{\"type\": \"chart\", \"data\": {\"chart_name\": \"Open Projects\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Task\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Timesheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project Billing Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Projects\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Time Tracking\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:46:04.874669",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "project",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Projects",
"links": [
{
@@ -201,15 +194,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:01.540145",
+ "modified": "2021-08-05 12:16:01.540147",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 3c60e3e..f8e8177 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -1,14 +1,10 @@
{
"css/erpnext.css": [
"public/less/erpnext.less",
- "public/less/hub.less",
"public/scss/call_popup.scss",
"public/scss/point-of-sale.scss",
"public/scss/hierarchy_chart.scss"
],
- "css/marketplace.css": [
- "public/less/hub.less"
- ],
"js/erpnext-web.min.js": [
"public/js/website_utils.js",
"public/js/shopping_cart.js"
@@ -17,9 +13,6 @@
"public/scss/website.scss",
"public/scss/shopping_cart.scss"
],
- "js/marketplace.min.js": [
- "public/js/hub/marketplace.js"
- ],
"js/erpnext.min.js": [
"public/js/conf.js",
"public/js/utils.js",
@@ -38,9 +31,9 @@
"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",
diff --git a/erpnext/public/images/hub_logo.svg b/erpnext/public/images/hub_logo.svg
deleted file mode 100644
index 4af4821..0000000
--- a/erpnext/public/images/hub_logo.svg
+++ /dev/null
@@ -1,112 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="330"
- height="345.43808"
- viewBox="0 0 87.312496 91.397155"
- version="1.1"
- id="svg4635"
- inkscape:version="0.92.2 5c3e80d, 2017-08-06"
- sodipodi:docname="hub-logo.svg"
- inkscape:export-filename="/home/raghu/Desktop/hub-logo.png"
- inkscape:export-xdpi="95.878258"
- inkscape:export-ydpi="95.878258">
- <defs
- id="defs4629" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.7"
- inkscape:cx="234.27717"
- inkscape:cy="167.57445"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- units="px"
- inkscape:window-width="1920"
- inkscape:window-height="1149"
- inkscape:window-x="0"
- inkscape:window-y="24"
- inkscape:window-maximized="1" />
- <metadata
- id="metadata4632">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- <cc:license
- rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
- </cc:Work>
- <cc:License
- rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
- <cc:permits
- rdf:resource="http://creativecommons.org/ns#Reproduction" />
- <cc:permits
- rdf:resource="http://creativecommons.org/ns#Distribution" />
- <cc:requires
- rdf:resource="http://creativecommons.org/ns#Notice" />
- <cc:requires
- rdf:resource="http://creativecommons.org/ns#Attribution" />
- <cc:permits
- rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
- <cc:requires
- rdf:resource="http://creativecommons.org/ns#ShareAlike" />
- </cc:License>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(121.51931,-138.66452)">
- <rect
- rx="13.229166"
- inkscape:export-ydpi="96"
- inkscape:export-xdpi="96"
- inkscape:export-filename="/home/raghu/Desktop/send/hub-02.png"
- style="opacity:1;vector-effect:none;fill:#89da29;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
- id="rect828"
- width="87.3125"
- height="87.3125"
- x="-121.51931"
- y="142.74918"
- ry="13.229166" />
- <path
- style="opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
- clip-path="none"
- d="m -121.51931,202.96343 v 13.86892 c 0,7.32897 5.90017,13.22917 13.22916,13.22917 h 60.854162 c 6.610072,0 12.056133,-4.80013 13.061216,-11.1187 -43.339761,0.1608 -54.359752,-16.03276 -87.144538,-15.97939 z"
- id="path830"
- inkscape:connector-curvature="0" />
- <path
- style="opacity:1;vector-effect:none;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
- clip-path="none"
- d="m -34.20681,202.96343 c -32.784694,-0.0533 -43.804846,16.14019 -87.14455,15.97939 1.00509,6.31857 6.45115,11.1187 13.06122,11.1187 h 60.854164 c 7.328992,0 13.229166,-5.9002 13.229166,-13.22917 z"
- id="path832"
- inkscape:connector-curvature="0" />
- <path
- id="path834"
- d="m -84.351263,175.75725 c -1.30945,0 -2.376091,1.06665 -2.376091,2.37608 v 10.02885 0.001 c 0.06583,4.83083 4.01156,8.73477 8.857351,8.73486 4.8718,5e-5 8.846821,-3.94421 8.871295,-8.81134 v -0.001 -9.95288 c 0,-1.30943 -1.066113,-2.37557 -2.375589,-2.37557 -1.309396,0 -2.376064,1.06614 -2.376064,2.37557 v 9.8888 c 0,2.26045 -1.858169,4.10983 -4.119642,4.10983 -2.263616,0 -4.105699,-1.82766 -4.105699,-4.08968 v -9.90844 c 0,-1.30943 -1.066138,-2.37608 -2.375561,-2.37608 z m -20.887107,0.0925 c -1.30943,0 -2.37609,1.06717 -2.37609,2.3766 v 16.45119 c 0,1.30944 1.06666,2.37609 2.37609,2.37609 1.30945,0 2.37556,-1.06665 2.37556,-2.37609 v -5.97327 h 8.22534 v 5.97327 c 0,1.30944 1.066641,2.37609 2.376091,2.37609 1.309423,0 2.375561,-1.06665 2.375561,-2.37609 v -16.45119 c 0,-1.30943 -1.066138,-2.3766 -2.375561,-2.3766 -1.30945,0 -2.376091,1.06717 -2.376091,2.3766 v 5.72627 h -8.22534 v -5.72627 c 0,-1.30943 -1.06611,-2.3766 -2.37556,-2.3766 z m 41.77419,0 c -0.654712,0 -1.248675,0.26711 -1.678967,0.69764 -0.05368,0.0537 -0.105119,0.10983 -0.153458,0.16846 v 5.3e-4 c -0.04839,0.0586 -0.09427,0.11929 -0.136949,0.18242 v 5.3e-4 c -0.256381,0.37936 -0.406691,0.83617 -0.406691,1.32705 v 16.45119 c 0,0.1635 0.01693,0.3242 0.04858,0.47852 0.09512,0.46331 0.32594,0.87828 0.64852,1.20096 0.161369,0.16136 0.345308,0.29938 0.547264,0.40928 v 0 c 0.134567,0.0732 0.276781,0.13403 0.425318,0.18035 v 0 c 0.148537,0.0463 0.303186,0.0783 0.462518,0.0946 v 0 c 0.07959,0.008 0.160708,0.0124 0.242358,0.0124 h 8.33181 c 0.08747,0 0.167931,-0.0145 0.251142,-0.0238 l 0.09509,0.005 c 0.06019,0.003 0.119407,0.005 0.178779,0.006 h 0.0037 0.0048 c 3.578305,-2e-5 6.487954,-2.90916 6.487981,-6.48747 v -0.001 c -0.0026,-1.51334 -0.578009,-2.9475 -1.540484,-4.10673 0.962448,-1.15892 1.537785,-2.59314 1.540484,-4.10621 v -0.001 c -2.7e-5,-3.57831 -2.909676,-6.48744 -6.487981,-6.48746 h -0.533294 z m 8.865103,4.75062 c 0.96393,0 1.736831,0.77394 1.736831,1.73788 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 v 0 h -0.532792 -5.955718 v -3.47317 h 5.956248 z m 0,8.21552 v 0 c 0.963507,5.3e-4 1.735799,0.77373 1.735799,1.73736 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 h -0.0048 l -0.533294,0.0119 h -5.951591 v -3.4742 h 5.959846 z"
- style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
- inkscape:connector-curvature="0" />
- <path
- style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.93750048;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
- d="m -77.859375,138.66406 c -9.653316,0 -18.439915,3.93483 -24.767575,10.28125 a 3.9691471,3.9691471 0 1 0 5.621091,5.60352 c 4.899576,-4.9141 11.6422,-7.94727 19.146484,-7.94727 7.501101,0 14.241542,3.03098 19.140625,7.94141 a 3.9691471,3.9691471 0 1 0 5.619141,-5.60547 c -6.327038,-6.34169 -15.110547,-10.27344 -24.759766,-10.27344 z"
- id="path838"
- inkscape:connector-curvature="0" />
- </g>
-</svg>
diff --git a/erpnext/public/js/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/buying.js b/erpnext/public/js/controllers/buying.js
index 86dadd3..d696ef5 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -165,45 +165,33 @@
}
qty(doc, cdt, cdn) {
- var item = frappe.get_doc(cdt, cdn);
if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && (doc.update_stock || doc.is_return))) {
- frappe.model.round_floats_in(item, ["qty", "received_qty"]);
-
- if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["qty", "received_qty"])){ return }
-
- if(!item.rejected_qty && item.qty) {
- item.received_qty = item.qty;
- }
-
- frappe.model.round_floats_in(item, ["qty", "received_qty"]);
- item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
- item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty);
+ this.calculate_received_qty(doc, cdt, cdn)
}
super.qty(doc, cdt, cdn);
}
+ rejected_qty(doc, cdt, cdn) {
+ this.calculate_received_qty(doc, cdt, cdn)
+ }
+
+ calculate_received_qty(doc, cdt, cdn){
+ var item = frappe.get_doc(cdt, cdn);
+ frappe.model.round_floats_in(item, ["qty", "rejected_qty"]);
+
+ if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["qty", "rejected_qty"])){ return }
+
+ let received_qty = flt(item.qty + item.rejected_qty, precision("received_qty", item));
+ let received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(received_qty);
+
+ frappe.model.set_value(cdt, cdn, "received_qty", received_qty);
+ frappe.model.set_value(cdt, cdn, "received_stock_qty", received_stock_qty);
+ }
+
batch_no(doc, cdt, cdn) {
super.batch_no(doc, cdt, cdn);
}
- received_qty(doc, cdt, cdn) {
- this.calculate_accepted_qty(doc, cdt, cdn)
- }
-
- rejected_qty(doc, cdt, cdn) {
- this.calculate_accepted_qty(doc, cdt, cdn)
- }
-
- calculate_accepted_qty(doc, cdt, cdn){
- var item = frappe.get_doc(cdt, cdn);
- frappe.model.round_floats_in(item, ["received_qty", "rejected_qty"]);
-
- if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["received_qty", "rejected_qty"])){ return }
-
- item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item));
- this.qty(doc, cdt, cdn);
- }
-
validate_negative_quantity(cdt, cdn, item, fieldnames){
if(!item || !fieldnames) { return }
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 702064f..7c1c8c7 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -81,6 +81,7 @@
this.initialize_taxes();
this.determine_exclusive_rate();
this.calculate_net_total();
+ this.calculate_shipping_charges();
this.calculate_taxes();
this.manipulate_grand_total_for_inclusive_tax();
this.calculate_totals();
@@ -137,7 +138,9 @@
var me = this;
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
- tax.item_wise_tax_detail = {};
+ if (!tax.dont_recompute_tax) {
+ tax.item_wise_tax_detail = {};
+ }
var tax_fields = ["total", "tax_amount_after_discount_amount",
"tax_amount_for_current_item", "grand_total_for_current_item",
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"];
@@ -264,8 +267,13 @@
me.frm.doc.net_total += item.net_amount;
me.frm.doc.base_net_total += item.base_net_amount;
});
+ }
+ calculate_shipping_charges() {
frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
+ if (frappe.meta.get_docfield(this.frm.doc.doctype, "shipping_rule", this.frm.doc.name)) {
+ this.shipping_rule();
+ }
}
add_taxes_from_item_tax_template(item_tax_map) {
@@ -421,7 +429,9 @@
current_tax_amount = tax_rate * item.qty;
}
- this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
+ if (!tax.dont_recompute_tax) {
+ this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
+ }
return current_tax_amount;
}
@@ -589,7 +599,9 @@
delete tax[fieldname];
});
- tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
+ if (!tax.dont_recompute_tax) {
+ tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
+ }
});
}
}
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 2538852..773d53c 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -345,26 +345,14 @@
}
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) {
@@ -375,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",
@@ -487,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;
@@ -613,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);
}
},
@@ -866,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);
@@ -1039,16 +1085,8 @@
return this.frm.call({
doc: this.frm.doc,
method: "apply_shipping_rule",
- callback: function(r) {
- if(!r.exc) {
- me.calculate_taxes_and_totals();
- }
- }
}).fail(() => this.frm.set_value('shipping_rule', ''));
}
- else {
- me.calculate_taxes_and_totals();
- }
}
set_margin_amount_based_on_currency(exchange_rate) {
@@ -1068,7 +1106,7 @@
$.each(this.frm.doc.taxes || [], function(i, d) {
if(d.charge_type == "Actual") {
frappe.model.set_value(d.doctype, d.name, "tax_amount",
- flt(d.tax_amount) / flt(exchange_rate));
+ flt(d.base_tax_amount) / flt(exchange_rate));
}
});
}
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index 9f7f29a..5259bdc 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -15,9 +15,9 @@
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";
import "./call_popup/call_popup";
import "./utils/dimension_tree_filter";
import "./telephony";
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index 0d79b10..1a309ba 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -113,15 +113,15 @@
"fieldname":"period_start_date",
"label": __("Start Date"),
"fieldtype": "Date",
- "hidden": 1,
- "reqd": 1
+ "reqd": 1,
+ "depends_on": "eval:doc.filter_based_on == 'Date Range'"
},
{
"fieldname":"period_end_date",
"label": __("End Date"),
"fieldtype": "Date",
- "hidden": 1,
- "reqd": 1
+ "reqd": 1,
+ "depends_on": "eval:doc.filter_based_on == 'Date Range'"
},
{
"fieldname":"from_fiscal_year",
@@ -129,7 +129,8 @@
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
- "reqd": 1
+ "reqd": 1,
+ "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
},
{
"fieldname":"to_fiscal_year",
@@ -137,7 +138,8 @@
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
- "reqd": 1
+ "reqd": 1,
+ "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
},
{
"fieldname": "periodicity",
diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js
index d0c935f..b643cca 100644
--- a/erpnext/public/js/help_links.js
+++ b/erpnext/public/js/help_links.js
@@ -5,7 +5,7 @@
frappe.help.help_links["Form/Rename Tool"] = [
{
label: "Bulk Rename",
- url: docsUrl + "user/manual/en/setting-up/data/bulk-rename",
+ url: docsUrl + "user/manual/en/using-erpnext/articles/bulk-rename",
},
];
@@ -59,10 +59,23 @@
},
];
-frappe.help.help_links["data-import-tool"] = [
+frappe.help.help_links["Form/Data Import"] = [
{
label: "Importing and Exporting Data",
- url: docsUrl + "user/manual/en/setting-up/data/data-import-tool",
+ url: docsUrl + "user/manual/en/setting-up/data/data-import",
+ },
+ {
+ label: "Overwriting Data from Data Import Tool",
+ url:
+ docsUrl +
+ "user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool",
+ },
+];
+
+frappe.help.help_links["List/Data Import"] = [
+ {
+ label: "Importing and Exporting Data",
+ url: docsUrl + "user/manual/en/setting-up/data/data-import",
},
{
label: "Overwriting Data from Data Import Tool",
@@ -101,14 +114,14 @@
},
];
-frappe.help.help_links["Form/Email Digest"] = [
+frappe.help.help_links["List/Print Heading"] = [
{
- label: "Email Digest",
- url: docsUrl + "user/manual/en/setting-up/email/email-digest",
+ label: "Print Heading",
+ url: docsUrl + "user/manual/en/setting-up/print/print-headings",
},
];
-frappe.help.help_links["List/Print Heading"] = [
+frappe.help.help_links["Form/Print Heading"] = [
{
label: "Print Heading",
url: docsUrl + "user/manual/en/setting-up/print/print-headings",
@@ -153,18 +166,25 @@
frappe.help.help_links["List/Notification"] = [
{
label: "Notification",
- url: docsUrl + "user/manual/en/setting-up/email/notifications",
+ url: docsUrl + "user/manual/en/setting-up/notifications",
},
];
frappe.help.help_links["Form/Notification"] = [
{
label: "Notification",
- url: docsUrl + "user/manual/en/setting-up/email/notifications",
+ url: docsUrl + "user/manual/en/setting-up/notifications",
},
];
-frappe.help.help_links["List/Email Digest"] = [
+frappe.help.help_links["Form/Email Digest"] = [
+ {
+ label: "Email Digest",
+ url: docsUrl + "user/manual/en/setting-up/email/email-digest",
+ },
+];
+
+frappe.help.help_links["Form/Email Digest"] = [
{
label: "Email Digest",
url: docsUrl + "user/manual/en/setting-up/email/email-digest",
@@ -174,7 +194,7 @@
frappe.help.help_links["List/Auto Email Report"] = [
{
label: "Auto Email Reports",
- url: docsUrl + "user/manual/en/setting-up/email/email-reports",
+ url: docsUrl + "user/manual/en/setting-up/email/auto-email-reports",
},
];
@@ -188,14 +208,7 @@
frappe.help.help_links["print-format-builder"] = [
{
label: "Print Format Builder",
- url: docsUrl + "user/manual/en/setting-up/print/print-settings",
- },
-];
-
-frappe.help.help_links["List/Print Heading"] = [
- {
- label: "Print Heading",
- url: docsUrl + "user/manual/en/setting-up/print/print-headings",
+ url: docsUrl + "user/manual/en/setting-up/print/print-format-builder",
},
];
@@ -300,7 +313,7 @@
},
{
label: "Recurring Sales Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
{
label: "Applying Discount",
@@ -315,7 +328,7 @@
},
{
label: "Recurring Sales Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
{
label: "Applying Discount",
@@ -344,14 +357,14 @@
frappe.help.help_links["Form/Product Bundle"] = [
{
label: "Product Bundle",
- url: docsUrl + "user/manual/en/selling/setup/product-bundle",
+ url: docsUrl + "user/manual/en/selling/product-bundle",
},
];
frappe.help.help_links["Form/Selling Settings"] = [
{
label: "Selling Settings",
- url: docsUrl + "user/manual/en/selling/setup/selling-settings",
+ url: docsUrl + "user/manual/en/selling/selling-settings",
},
];
@@ -397,7 +410,7 @@
},
{
label: "Recurring Purchase Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
@@ -420,7 +433,7 @@
},
{
label: "Recurring Purchase Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
{
label: "Subcontracting",
@@ -435,24 +448,17 @@
},
];
-frappe.help.help_links["List/POS Profile"] = [
- {
- label: "POS Profile",
- url: docsUrl + "user/manual/en/setting-up/pos-setting",
- },
-];
-
frappe.help.help_links["List/Price List"] = [
{
label: "Price List",
- url: docsUrl + "user/manual/en/setting-up/price-lists",
+ url: docsUrl + "user/manual/en/stock/price-lists",
},
];
frappe.help.help_links["List/Authorization Rule"] = [
{
label: "Authorization Rule",
- url: docsUrl + "user/manual/en/setting-up/authorization-rule",
+ url: docsUrl + "user/manual/en/customize-erpnext/authorization-rule",
},
];
@@ -468,27 +474,14 @@
label: "Stock Reconciliation",
url:
docsUrl +
- "user/manual/en/setting-up/stock-reconciliation-for-non-serialized-item",
+ "user/manual/en/stock/stock-reconciliation",
},
];
frappe.help.help_links["Tree/Territory"] = [
{
label: "Territory",
- url: docsUrl + "user/manual/en/setting-up/territory",
- },
-];
-
-frappe.help.help_links["Form/Dropbox Backup"] = [
- {
- label: "Dropbox Backup",
- url: docsUrl + "user/manual/en/setting-up/third-party-backups",
- },
- {
- label: "Setting Up Dropbox Backup",
- url:
- docsUrl +
- "user/manual/en/setting-up/articles/setting-up-dropbox-backups",
+ url: docsUrl + "user/manual/en/selling/territory",
},
];
@@ -502,12 +495,6 @@
url: docsUrl + "user/manual/en/setting-up/company-setup",
},
{
- label: "Managing Multiple Companies",
- url:
- docsUrl +
- "user/manual/en/setting-up/articles/managing-multiple-companies",
- },
- {
label: "Delete All Related Transactions for a Company",
url:
docsUrl +
@@ -517,21 +504,6 @@
//Accounts
-frappe.help.help_links["modules/Accounts"] = [
- {
- label: "Introduction to Accounts",
- url: docsUrl + "user/manual/en/accounts/",
- },
- {
- label: "Chart of Accounts",
- url: docsUrl + "user/manual/en/accounts/chart-of-accounts.html",
- },
- {
- label: "Multi Currency Accounting",
- url: docsUrl + "user/manual/en/accounts/multi-currency-accounting",
- },
-];
-
frappe.help.help_links["Tree/Account"] = [
{
label: "Chart of Accounts",
@@ -552,7 +524,7 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balance",
},
{
label: "Sales Return",
@@ -560,7 +532,7 @@
},
{
label: "Recurring Sales Invoice",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
@@ -571,7 +543,7 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balances",
},
{
label: "Sales Return",
@@ -579,21 +551,28 @@
},
{
label: "Recurring Sales Invoice",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
-frappe.help.help_links["pos"] = [
+frappe.help.help_links["point-of-sale"] = [
{
label: "Point of Sale Invoice",
- url: docsUrl + "user/manual/en/accounts/point-of-sale-pos-invoice",
+ url: docsUrl + "user/manual/en/accounts/point-of-sales",
},
];
frappe.help.help_links["List/POS Profile"] = [
{
label: "Point of Sale Profile",
- url: docsUrl + "user/manual/en/setting-up/pos-setting",
+ url: docsUrl + "user/manual/en/accounts/pos-profile",
+ },
+];
+
+frappe.help.help_links["Form/POS Profile"] = [
+ {
+ label: "POS Profile",
+ url: docsUrl + "user/manual/en/accounts/pos-profile",
},
];
@@ -604,11 +583,11 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balance",
},
{
label: "Recurring Purchase Invoice",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
@@ -623,7 +602,7 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balance",
},
];
@@ -644,7 +623,7 @@
frappe.help.help_links["List/Asset"] = [
{
label: "Managing Fixed Assets",
- url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+ url: docsUrl + "user/manual/en/asset",
},
];
@@ -659,6 +638,8 @@
{ label: "Budgeting", url: docsUrl + "user/manual/en/accounts/budgeting" },
];
+//Stock
+
frappe.help.help_links["List/Item"] = [
{ label: "Item", url: docsUrl + "user/manual/en/stock/item" },
{
@@ -676,7 +657,7 @@
},
{
label: "Managing Fixed Assets",
- url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+ url: docsUrl + "user/manual/en/asset",
},
{
label: "Item Codification",
@@ -711,7 +692,7 @@
},
{
label: "Managing Fixed Assets",
- url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+ url: docsUrl + "user/manual/en/asset",
},
{
label: "Item Codification",
@@ -771,10 +752,6 @@
url:
docsUrl + "user/manual/en/stock/articles/track-items-using-barcode",
},
- {
- label: "Subcontracting",
- url: docsUrl + "user/manual/en/manufacturing/subcontracting",
- },
];
frappe.help.help_links["List/Installation Note"] = [
@@ -784,21 +761,10 @@
},
];
-frappe.help.help_links["Tree"] = [
- {
- label: "Managing Tree Structure Masters",
- url:
- docsUrl +
- "user/manual/en/setting-up/articles/managing-tree-structure-masters",
- },
-];
-
frappe.help.help_links["List/Budget"] = [
{ label: "Budgeting", url: docsUrl + "user/manual/en/accounts/budgeting" },
];
-//Stock
-
frappe.help.help_links["List/Material Request"] = [
{
label: "Material Request",
@@ -861,6 +827,10 @@
{ label: "Serial No", url: docsUrl + "user/manual/en/stock/serial-no" },
];
+frappe.help.help_links["List/Batch"] = [
+ { label: "Batch", url: docsUrl + "user/manual/en/stock/batch" },
+];
+
frappe.help.help_links["Form/Batch"] = [
{ label: "Batch", url: docsUrl + "user/manual/en/stock/batch" },
];
@@ -868,35 +838,35 @@
frappe.help.help_links["Form/Packing Slip"] = [
{
label: "Packing Slip",
- url: docsUrl + "user/manual/en/stock/tools/packing-slip",
+ url: docsUrl + "user/manual/en/stock/packing-slip",
},
];
frappe.help.help_links["Form/Quality Inspection"] = [
{
label: "Quality Inspection",
- url: docsUrl + "user/manual/en/stock/tools/quality-inspection",
+ url: docsUrl + "user/manual/en/stock/quality-inspection",
},
];
frappe.help.help_links["Form/Landed Cost Voucher"] = [
{
label: "Landed Cost Voucher",
- url: docsUrl + "user/manual/en/stock/tools/landed-cost-voucher",
+ url: docsUrl + "user/manual/en/stock/landed-cost-voucher",
},
];
frappe.help.help_links["Tree/Item Group"] = [
{
label: "Item Group",
- url: docsUrl + "user/manual/en/stock/setup/item-group",
+ url: docsUrl + "user/manual/en/stock/item-group",
},
];
frappe.help.help_links["Form/Item Attribute"] = [
{
label: "Item Attribute",
- url: docsUrl + "user/manual/en/stock/setup/item-attribute",
+ url: docsUrl + "user/manual/en/stock/item-attribute",
},
];
@@ -911,7 +881,7 @@
frappe.help.help_links["Form/Stock Reconciliation"] = [
{
label: "Opening Stock Entry",
- url: docsUrl + "user/manual/en/stock/opening-stock",
+ url: docsUrl + "user/manual/en/stock/stock-reconciliation",
},
];
@@ -938,13 +908,13 @@
];
frappe.help.help_links["Form/Campaign"] = [
- { label: "Campaign", url: docsUrl + "user/manual/en/CRM/setup/campaign" },
+ { label: "Campaign", url: docsUrl + "user/manual/en/CRM/campaign" },
];
frappe.help.help_links["Tree/Sales Person"] = [
{
label: "Sales Person",
- url: docsUrl + "user/manual/en/CRM/setup/sales-person",
+ url: docsUrl + "user/manual/en/CRM/sales-person",
},
];
@@ -953,30 +923,13 @@
label: "Sales Person Target",
url:
docsUrl +
- "user/manual/en/selling/setup/sales-person-target-allocation",
+ "user/manual/en/selling/sales-person-target-allocation",
},
-];
-
-//Support
-
-frappe.help.help_links["List/Feedback Trigger"] = [
{
- label: "Feedback Trigger",
- url: docsUrl + "user/manual/en/setting-up/feedback/setting-up-feedback",
- },
-];
-
-frappe.help.help_links["List/Feedback Request"] = [
- {
- label: "Feedback Request",
- url: docsUrl + "user/manual/en/setting-up/feedback/submit-feedback",
- },
-];
-
-frappe.help.help_links["List/Feedback Request"] = [
- {
- label: "Feedback Request",
- url: docsUrl + "user/manual/en/setting-up/feedback/submit-feedback",
+ label: "Sales Person in Transactions",
+ url:
+ docsUrl +
+ "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions",
},
];
@@ -1019,7 +972,7 @@
frappe.help.help_links["Form/BOM Update Tool"] = [
{
label: "BOM Update Tool",
- url: docsUrl + "user/manual/en/manufacturing/tools/bom-update-tool",
+ url: docsUrl + "user/manual/en/manufacturing/bom-update-tool",
},
];
@@ -1036,7 +989,7 @@
},
];
-frappe.help.help_links["Form/Custom Field"] = [
+frappe.help.help_links["List/Custom Field"] = [
{
label: "Custom Field",
url: docsUrl + "user/manual/en/customize-erpnext/custom-field",
diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
index 23ec2fd..831626a 100644
--- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
@@ -63,12 +63,10 @@
});
node.parent.append(node_card);
- node.$link = $(`#${node.id}`);
+ node.$link = $(`[id="${node.id}"]`);
}
show() {
- frappe.breadcrumbs.add('HR');
-
this.setup_actions();
if ($(`[data-fieldname="company"]`).length) return;
let me = this;
@@ -83,8 +81,9 @@
reqd: 1,
change: () => {
me.company = undefined;
+ $('#hierarchy-chart-wrapper').remove();
- if (company.get_value() && me.company != company.get_value()) {
+ if (company.get_value()) {
me.company = company.get_value();
// svg for connectors
@@ -92,6 +91,8 @@
me.setup_hierarchy();
me.render_root_nodes();
me.all_nodes_expanded = false;
+ } else {
+ frappe.throw(__('Please select a company first.'));
}
}
});
@@ -172,11 +173,11 @@
</ul>`);
this.page.main
- .find('#hierarchy-chart-wrapper')
+ .find('#hierarchy-chart')
+ .empty()
.append(this.$hierarchy);
this.nodes = {};
- this.all_nodes_expanded = false;
}
make_svg_markers() {
@@ -203,6 +204,8 @@
<g id="connectors" fill="none">
</g>
</svg>
+ <div id="hierarchy-chart">
+ </div>
</div>`);
}
@@ -219,7 +222,10 @@
let expand_node = undefined;
let node = undefined;
- $.each(r.message, (i, data) => {
+ $.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')),
@@ -257,7 +263,7 @@
this.refresh_connectors(node.parent_id);
// rebuild incoming connections
- let grandparent = $(`#${node.parent_id}`).attr('data-parent');
+ let grandparent = $(`[id="${node.parent_id}"]`).attr('data-parent');
this.refresh_connectors(grandparent);
}
@@ -276,7 +282,7 @@
show_active_path(node) {
// mark node parent on active path
- $(`#${node.parent_id}`).addClass('active-path');
+ $(`[id="${node.parent_id}"]`).addClass('active-path');
}
load_children(node, deep=false) {
@@ -290,7 +296,7 @@
() => frappe.dom.freeze(),
() => this.setup_hierarchy(),
() => this.render_root_nodes(true),
- () => this.get_all_nodes(node.id, node.name),
+ () => this.get_all_nodes(),
(data_list) => this.render_children_of_all_nodes(data_list),
() => frappe.dom.unfreeze()
]);
@@ -311,7 +317,7 @@
render_child_nodes(node, child_nodes) {
const last_level = this.$hierarchy.find('.level:last').index();
- const current_level = $(`#${node.id}`).parent().parent().parent().index();
+ const current_level = $(`[id="${node.id}"]`).parent().parent().parent().index();
if (last_level === current_level) {
this.$hierarchy.append(`
@@ -328,10 +334,12 @@
if (child_nodes) {
$.each(child_nodes, (_i, data) => {
- this.add_node(node, data);
- setTimeout(() => {
- this.add_connector(node.id, data.id);
- }, 250);
+ if (!$(`[id="${data.id}"]`).length) {
+ this.add_node(node, data);
+ setTimeout(() => {
+ this.add_connector(node.id, data.id);
+ }, 250);
+ }
});
}
}
@@ -341,15 +349,13 @@
node.expanded = true;
}
- get_all_nodes(node_id, node_name) {
+ get_all_nodes() {
return new Promise(resolve => {
frappe.call({
method: 'erpnext.utilities.hierarchy_chart.get_all_nodes',
args: {
method: this.method,
- company: this.company,
- parent: node_id,
- parent_name: node_name
+ company: this.company
},
callback: (r) => {
resolve(r.message);
@@ -378,7 +384,7 @@
node.$children = $('<ul class="node-children"></ul>');
const last_level = this.$hierarchy.find('.level:last').index();
- const node_level = $(`#${node.id}`).parent().parent().parent().index();
+ const node_level = $(`[id="${node.id}"]`).parent().parent().parent().index();
if (last_level === node_level) {
this.$hierarchy.append(`
@@ -485,7 +491,7 @@
set_path_attributes(path, parent_id, child_id) {
path.setAttribute("data-parent", parent_id);
path.setAttribute("data-child", child_id);
- const parent = $(`#${parent_id}`);
+ const parent = $(`[id="${parent_id}"]`);
if (parent.hasClass('active')) {
path.setAttribute("class", "active-connector");
@@ -509,7 +515,7 @@
}
collapse_previous_level_nodes(node) {
- let node_parent = $(`#${node.parent_id}`);
+ let node_parent = $(`[id="${node.parent_id}"]`);
let previous_level_nodes = node_parent.parent().parent().children('li');
let node_card = undefined;
@@ -541,7 +547,7 @@
setup_node_click_action(node) {
let me = this;
- let node_element = $(`#${node.id}`);
+ let node_element = $(`[id="${node.id}"]`);
node_element.click(function() {
const is_sibling = me.selected_node.parent_id === node.parent_id;
@@ -559,7 +565,7 @@
}
setup_edit_node_action(node) {
- let node_element = $(`#${node.id}`);
+ let node_element = $(`[id="${node.id}"]`);
let me = this;
node_element.find('.btn-edit-node').click(function() {
@@ -568,7 +574,7 @@
}
remove_levels_after_node(node) {
- let level = $(`#${node.id}`).parent().parent().parent().index();
+ let level = $(`[id="${node.id}"]`).parent().parent().parent().index();
level = $('.hierarchy > li:eq('+ level + ')');
level.nextAll('li').remove();
@@ -591,7 +597,7 @@
const parent = $(path).data('parent');
const child = $(path).data('child');
- if ($(`#${parent}`).length && $(`#${child}`).length)
+ 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
index b1b78c0..0a8ba78 100644
--- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
@@ -54,13 +54,11 @@
});
node.parent.append(node_card);
- node.$link = $(`#${node.id}`);
+ node.$link = $(`[id="${node.id}"]`);
node.$link.addClass('mobile-node');
}
show() {
- frappe.breadcrumbs.add('HR');
-
let me = this;
if ($(`[data-fieldname="company"]`).length) return;
@@ -186,7 +184,7 @@
this.refresh_connectors(node.parent_id, node.id);
// rebuild incoming connections of parent
- let grandparent = $(`#${node.parent_id}`).attr('data-parent');
+ let grandparent = $(`[id="${node.parent_id}"]`).attr('data-parent');
this.refresh_connectors(grandparent, node.parent_id);
}
@@ -223,7 +221,7 @@
show_active_path(node) {
// mark node parent on active path
- $(`#${node.parent_id}`).addClass('active-path');
+ $(`[id="${node.parent_id}"]`).addClass('active-path');
}
load_children(node) {
@@ -258,7 +256,7 @@
if (child_nodes) {
$.each(child_nodes, (_i, data) => {
this.add_node(node, data);
- $(`#${data.id}`).addClass('active-child');
+ $(`[id="${data.id}"]`).addClass('active-child');
setTimeout(() => {
this.add_connector(node.id, data.id);
@@ -295,9 +293,9 @@
let connector = undefined;
- if ($(`#${parent_id}`).hasClass('active')) {
+ if ($(`[id="${parent_id}"]`).hasClass('active')) {
connector = this.get_connector_for_active_node(parent_node, child_node);
- } else if ($(`#${parent_id}`).hasClass('active-path')) {
+ } else if ($(`[id="${parent_id}"]`).hasClass('active-path')) {
connector = this.get_connector_for_collapsed_node(parent_node, child_node);
}
@@ -353,7 +351,7 @@
set_path_attributes(path, parent_id, child_id) {
path.setAttribute("data-parent", parent_id);
path.setAttribute("data-child", child_id);
- const parent = $(`#${parent_id}`);
+ const parent = $(`[id="${parent_id}"]`);
if (parent.hasClass('active')) {
path.setAttribute("class", "active-connector");
@@ -376,7 +374,7 @@
setup_node_click_action(node) {
let me = this;
- let node_element = $(`#${node.id}`);
+ let node_element = $(`[id="${node.id}"]`);
node_element.click(function() {
let el = undefined;
@@ -400,7 +398,7 @@
}
setup_edit_node_action(node) {
- let node_element = $(`#${node.id}`);
+ let node_element = $(`[id="${node.id}"]`);
let me = this;
node_element.find('.btn-edit-node').click(function() {
@@ -514,7 +512,7 @@
}
remove_levels_after_node(node) {
- let level = $(`#${node.id}`).parent().parent().index();
+ let level = $(`[id="${node.id}"]`).parent().parent().index();
level = $('.hierarchy-mobile > li:eq('+ level + ')');
level.nextAll('li').remove();
@@ -535,7 +533,7 @@
const parent = $(path).data('parent');
const child = $(path).data('child');
- if ($(`#${parent}`).length && $(`#${child}`).length)
+ if ($(`[id="${parent}"]`).length && $(`[id="${child}"]`).length)
return;
$(path).remove();
diff --git a/erpnext/public/js/hub/PageContainer.vue b/erpnext/public/js/hub/PageContainer.vue
deleted file mode 100644
index 54c3597..0000000
--- a/erpnext/public/js/hub/PageContainer.vue
+++ /dev/null
@@ -1,119 +0,0 @@
-<template>
- <div class="hub-page-container">
- <component :is="current_page.component" :key="current_page.key"></component>
- </div>
-</template>
-
-<script>
-
-import Home from './pages/Home.vue';
-import Search from './pages/Search.vue';
-import Category from './pages/Category.vue';
-import SavedItems from './pages/SavedItems.vue';
-import FeaturedItems from './pages/FeaturedItems.vue';
-import PublishedItems from './pages/PublishedItems.vue';
-import Item from './pages/Item.vue';
-import Seller from './pages/Seller.vue';
-import SellerItems from './pages/SellerItems.vue';
-import Publish from './pages/Publish.vue';
-import Buying from './pages/Buying.vue';
-import Selling from './pages/Selling.vue';
-import Messages from './pages/Messages.vue';
-import NotFound from './pages/NotFound.vue';
-
-function get_route_map() {
- const read_only_routes = {
- 'marketplace/home': Home,
- 'marketplace/search/:category/:keyword': Search,
- 'marketplace/category/:category': Category,
- 'marketplace/item/:item': Item,
- 'marketplace/seller/:seller': Seller,
- 'marketplace/seller/:seller/items': SellerItems,
- 'marketplace/not-found': NotFound,
- }
- const registered_routes = {
- 'marketplace/profile': Seller,
- 'marketplace/saved-items': SavedItems,
- 'marketplace/featured-items': FeaturedItems,
- 'marketplace/publish': Publish,
- 'marketplace/published-items': PublishedItems,
- 'marketplace/buying': Buying,
- 'marketplace/buying/:item': Messages,
- 'marketplace/selling': Selling,
- 'marketplace/selling/:buyer/:item': Messages
- }
-
- return hub.is_seller_registered()
- ? Object.assign({}, read_only_routes, registered_routes)
- : read_only_routes;
-}
-
-export default {
- data() {
- return {
- current_page: this.get_current_page()
- }
- },
- mounted() {
- frappe.route.on('change', () => {
- if (frappe.get_route()[0] === 'marketplace') {
- this.set_current_page();
- frappe.utils.scroll_to(0);
- }
- });
- },
- methods: {
- set_current_page() {
- this.current_page = this.get_current_page();
- },
- get_current_page() {
- const route_map = get_route_map();
- const curr_route = frappe.get_route_str();
- let route = Object.keys(route_map).filter(route => route == curr_route)[0];
- if (!route) {
- // find route by matching it with dynamic part
- const curr_route_parts = curr_route.split('/');
- const weighted_routes = Object.keys(route_map)
- .map(route_str => route_str.split('/'))
- .filter(route_parts => route_parts.length === curr_route_parts.length)
- .reduce((obj, route_parts) => {
- const key = route_parts.join('/');
- let weight = 0;
- route_parts.forEach((part, i) => {
- const curr_route_part = curr_route_parts[i];
- if (part === curr_route_part || part.includes(':')) {
- weight += 1;
- }
- });
-
- obj[key] = weight;
- return obj;
- }, {});
-
- // get the route with the highest weight
- for (let key in weighted_routes) {
- const route_weight = weighted_routes[key];
- if (route_weight === curr_route_parts.length) {
- route = key;
- break;
- } else {
- route = null;
- }
- }
- }
-
- if (!route) {
- return {
- key: 'not-found',
- component: NotFound
- };
- }
-
- return {
- key: curr_route,
- component: route_map[route]
- }
- }
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/Sidebar.vue b/erpnext/public/js/hub/Sidebar.vue
deleted file mode 100644
index 66c291e..0000000
--- a/erpnext/public/js/hub/Sidebar.vue
+++ /dev/null
@@ -1,110 +0,0 @@
-<template>
- <div ref="sidebar-container">
- <ul class="list-unstyled hub-sidebar-group" data-nav-buttons>
- <li class="hub-sidebar-item" v-for="item in items" :key="item.label" v-route="item.route" v-show="item.condition === undefined || item.condition()">
- {{ item.label }}
- </li>
- </ul>
- <ul class="list-unstyled hub-sidebar-group" data-categories>
- <li class="hub-sidebar-item is-title bold text-muted">
- {{ __('Categories') }}
- </li>
- <li class="hub-sidebar-item" v-for="category in categories" :key="category.label" v-route="category.route">
- {{ category.label }}
- </li>
- </ul>
- </div>
-</template>
-<script>
-export default {
- data() {
- return {
- hub_registered: hub.is_user_registered(),
- items: [
- {
- label: __('Browse'),
- route: 'marketplace/home'
- },
- {
- label: __('Saved Items'),
- route: 'marketplace/saved-items',
- condition: () => this.hub_registered
- },
- {
- label: __('Your Featured Items'),
- route: 'marketplace/featured-items',
- condition: () => this.hub_registered
- },
- {
- label: __('Your Profile'),
- route: 'marketplace/profile',
- condition: () => this.hub_registered
- },
- {
- label: __('Your Items'),
- route: 'marketplace/published-items',
- condition: () => this.hub_registered
- },
- {
- label: __('Publish Items'),
- route: 'marketplace/publish',
- condition: () => this.hub_registered
- },
- {
- label: __('Selling'),
- route: 'marketplace/selling',
- condition: () => this.hub_registered
- },
- {
- label: __('Buying'),
- route: 'marketplace/buying',
- condition: () => this.hub_registered
- },
- ],
- categories: []
- }
- },
- created() {
- this.get_categories()
- .then(categories => {
- this.categories = categories.map(c => {
- return {
- label: __(c.name),
- route: 'marketplace/category/' + c.name
- }
- });
- this.categories.unshift({
- label: __('All'),
- route: 'marketplace/home'
- });
- this.$nextTick(() => {
- this.update_sidebar_state();
- });
- });
-
- erpnext.hub.on('seller-registered', () => {
- this.hub_registered = true;
- })
- },
- mounted() {
- this.update_sidebar_state();
- frappe.route.on('change', () => this.update_sidebar_state());
- },
- methods: {
- get_categories() {
- return hub.call('get_categories');
- },
- update_sidebar_state() {
- const container = $(this.$refs['sidebar-container']);
- const route = frappe.get_route();
- const route_str = route.join('/');
- const part_route_str = route.slice(0, 2).join('/');
- const $sidebar_item = container.find(`[data-route="${route_str}"], [data-route="${part_route_str}"]`);
-
- const $siblings = container.find('[data-route]');
- $siblings.removeClass('active').addClass('text-muted');
- $sidebar_item.addClass('active').removeClass('text-muted');
- },
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/components/CommentInput.vue b/erpnext/public/js/hub/components/CommentInput.vue
deleted file mode 100644
index 31562c7..0000000
--- a/erpnext/public/js/hub/components/CommentInput.vue
+++ /dev/null
@@ -1,39 +0,0 @@
-<template>
- <div>
- <div ref="comment-input"></div>
- <div class="level">
- <div class="level-left">
- <span class="text-muted">{{ __('Ctrl + Enter to submit') }}</span>
- </div>
- <div class="level-right">
- <button class="btn btn-primary btn-xs" @click="submit_input">{{ __('Submit') }}</button>
- </div>
- </div>
- </div>
-</template>
-<script>
-export default {
- mounted() {
- this.make_input();
- },
- methods: {
- make_input() {
- this.message_input = frappe.ui.form.make_control({
- parent: this.$refs['comment-input'],
- on_submit: (message) => {
- this.message_input.reset();
- this.$emit('change', message);
- },
- only_input: true,
- no_wrapper: true
- });
- },
- submit_input() {
- if (!this.message_input) return;
- const value = this.message_input.get_value();
- if (!value) return;
- this.message_input.submit();
- }
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/components/DetailHeaderItem.vue b/erpnext/public/js/hub/components/DetailHeaderItem.vue
deleted file mode 100644
index a6c5f06..0000000
--- a/erpnext/public/js/hub/components/DetailHeaderItem.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<template>
- <p class="text-muted" v-if="!Array.isArray(this.header_items)" v-html="header_items"></p>
- <p class="text-muted" v-else>
- <span v-for="(header_item , index) in header_items" :key="index">
- <span v-if="index" v-html="spacer"></span>
- <span v-if="typeof(header_item) == 'string'" v-html="header_item"></span>
- <a v-else-if="typeof(header_item) == 'object'" @click="header_item.on_click(header_item.value)" v-html="header_item.value"></a>
- </span>
- </p>
-</template>
-
-<script>
-
-const spacer = '<span aria-hidden="true"> · </span>';
-
-export default {
- name: 'detail-header-item',
- props: ['value'],
- data() {
- return {
- header_items: this.value,
- spacer: spacer
- }
- },
-}
-</script>
diff --git a/erpnext/public/js/hub/components/DetailView.vue b/erpnext/public/js/hub/components/DetailView.vue
deleted file mode 100644
index 942c1eb..0000000
--- a/erpnext/public/js/hub/components/DetailView.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-<template>
- <div class="hub-item-container">
- <div class="row visible-xs">
- <div class="col-xs-12 margin-bottom">
- <button class="btn btn-xs btn-default" data-route="marketplace/home">{{ back_to_home_text }}</button>
- </div>
- </div>
-
- <div v-if="show_skeleton" class="row margin-bottom">
- <div class="col-md-3">
- <div class="hub-item-skeleton-image"></div>
- </div>
- <div class="col-md-6">
- <h2 class="hub-skeleton" style="width: 75%;">Name</h2>
- <div class="text-muted">
- <p class="hub-skeleton" style="width: 35%;">Details</p>
- <p class="hub-skeleton" style="width: 50%;">Ratings</p>
- </div>
- <hr>
- <div class="hub-item-description">
- <p class="hub-skeleton">Desc</p>
- <p class="hub-skeleton" style="width: 85%;">Desc</p>
- </div>
- </div>
- </div>
-
- <div v-else>
- <div class="row margin-bottom">
- <div class="col-md-3">
- <div class="hub-item-image">
- <base-image :src="image" :alt="title" />
- </div>
- </div>
- <div class="col-md-8" style='padding-left: 30px;'>
- <h2>{{ title }}</h2>
- <div class="text-muted">
- <slot name="detail-header-item"></slot>
- </div>
- </div>
-
- <div v-if="menu_items && menu_items.length" class="col-md-1">
- <div class="dropdown pull-right hub-item-dropdown">
- <a class="dropdown-toggle btn btn-xs btn-default" data-toggle="dropdown">
- <span class="caret"></span>
- </a>
- <ul class="dropdown-menu dropdown-right" role="menu">
- <li v-for="menu_item in menu_items"
- v-if="menu_item.condition"
- :key="menu_item.label"
- >
- <a @click="menu_item.action">{{ menu_item.label }}</a>
- </li>
- </ul>
- </div>
- </div>
- </div>
- <div v-for="section in sections" class="row hub-item-description margin-bottom"
- :key="section.title"
- >
- <h6 class="col-md-12 margin-top">
- <b class="text-muted">{{ section.title }}</b>
- </h6>
- <p class="col-md-12" v-html="section.content">
- </p>
- </div>
- </div>
-
- </div>
-</template>
-
-<script>
-
-export default {
- name: 'detail-view',
- props: ['title', 'image', 'sections', 'show_skeleton', 'menu_items'],
- data() {
- return {
- back_to_home_text: __('Back to Home')
- }
- },
- computed: {}
-}
-</script>
-
-<style lang="less" scoped>
-</style>
diff --git a/erpnext/public/js/hub/components/EmptyState.vue b/erpnext/public/js/hub/components/EmptyState.vue
deleted file mode 100644
index e3a33a0..0000000
--- a/erpnext/public/js/hub/components/EmptyState.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-<template>
- <div class="empty-state flex flex-column"
- :class="{ 'bordered': bordered, 'align-center': centered, 'justify-center': centered }"
- :style="{ height: height + 'px' }"
- >
- <p class="text-muted" v-html="message" ></p>
- <p v-if="action">
- <button class="btn btn-default btn-xs"
- @click="action.on_click"
- >
- {{ action.label }}
- </button>
- </p>
- </div>
-</template>
-
-<script>
-
-export default {
- name: 'empty-state',
- props: {
- message: String,
- bordered: Boolean,
- height: Number,
- action: Object,
- centered: {
- type: Boolean,
- default: true
- }
- }
-}
-</script>
-
-<style lang="less">
- @import "../../../../../../frappe/frappe/public/less/variables.less";
-
- .empty-state {
- height: 500px;
- }
-
- .empty-state.bordered {
- border-radius: 4px;
- border: 1px solid @border-color;
- border-style: dashed;
-
- // bad, due to item card column layout, that is inner 15px margin
- margin: 0 15px;
- }
-
-</style>
diff --git a/erpnext/public/js/hub/components/Image.vue b/erpnext/public/js/hub/components/Image.vue
deleted file mode 100644
index 9acf421..0000000
--- a/erpnext/public/js/hub/components/Image.vue
+++ /dev/null
@@ -1,40 +0,0 @@
-<template>
- <div class="hub-image">
- <img :src="src" :alt="alt" v-show="!is_loading && !is_broken"/>
- <div class="hub-image-loading" v-if="is_loading">
- <span class="octicon octicon-cloud-download"></span>
- </div>
- <div class="hub-image-broken" v-if="is_broken">
- <span class="octicon octicon-file-media"></span>
- </div>
- </div>
-</template>
-<script>
-export default {
- name: 'Image',
- props: ['src', 'alt'],
- data() {
- return {
- is_loading: true,
- is_broken: false
- }
- },
- created() {
- this.handle_image();
- },
- methods: {
- handle_image() {
- let img = new Image();
- img.src = this.src;
-
- img.onload = () => {
- this.is_loading = false;
- };
- img.onerror = () => {
- this.is_loading = false;
- this.is_broken = true;
- };
- }
- }
-};
-</script>
diff --git a/erpnext/public/js/hub/components/ItemCard.vue b/erpnext/public/js/hub/components/ItemCard.vue
deleted file mode 100644
index 675ad86..0000000
--- a/erpnext/public/js/hub/components/ItemCard.vue
+++ /dev/null
@@ -1,142 +0,0 @@
-<template>
- <div v-if="seen" class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
- <div class="hub-card"
- @click="on_click(item_id)"
- >
- <div class="hub-card-header flex justify-between">
- <div class="ellipsis" :style="{ width: '85%' }">
- <div class="hub-card-title ellipsis bold">{{ title }}</div>
- <div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div>
- </div>
- <i v-if="allow_clear"
- class="octicon octicon-x text-extra-muted"
- @click.stop="$emit('remove-item', item_id)"
- >
- </i>
- </div>
- <div class="hub-card-body">
- <base-image class="hub-card-image" :src="item.image" :alt="title" />
- <div class="hub-card-overlay">
- <div v-if="is_local" class="hub-card-overlay-body">
- <div class="hub-card-overlay-button">
- <button class="btn btn-default zoom-view">
- <i class="octicon octicon-pencil text-muted"></i>
- </button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
-</template>
-
-<script>
-
-export default {
- name: 'item-card',
- props: ['item', 'item_id_fieldname', 'is_local', 'on_click', 'allow_clear', 'seen'],
- computed: {
- title() {
- const item_name = this.item.item_name || this.item.name;
- return strip_html(item_name);
- },
- subtitle() {
- const dot_spacer = '<span aria-hidden="true"> · </span>';
- if(this.is_local){
- return comment_when(this.item.creation);
- } else {
- let subtitle_items = [comment_when(this.item.creation)];
- const rating = this.item.average_rating;
-
- if (rating > 0) {
- subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
- }
-
- subtitle_items.push(this.item.company);
-
- return subtitle_items.join(dot_spacer);
- }
- },
- item_id() {
- return this.item[this.item_id_fieldname];
- }
- }
-}
-</script>
-
-<style lang="less" scoped>
- @import "../../../../../../frappe/frappe/public/less/variables.less";
-
- .hub-card {
- margin-bottom: 25px;
- position: relative;
- border: 1px solid @border-color;
- border-radius: 4px;
- overflow: hidden;
- cursor: pointer;
-
- &:hover .hub-card-overlay {
- display: block;
- }
-
- .octicon-x {
- display: block;
- font-size: 20px;
- margin-left: 10px;
- cursor: pointer;
- }
- }
-
- .hub-card.closable {
- .octicon-x {
- display: block;
- }
- }
-
- .hub-card.is-local {
- &.active {
- .hub-card-header {
- background-color: #f4ffe5;
- }
- }
- }
-
- .hub-card-header {
- position: relative;
- padding: 12px 15px;
- height: 60px;
- border-bottom: 1px solid @border-color;
- }
-
- .hub-card-body {
- position: relative;
- height: 200px;
- }
-
- .hub-card-overlay {
- display: none;
- position: absolute;
- top: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.05);
- }
-
- .hub-card-overlay-body {
- position: relative;
- height: 100%;
- }
-
- .hub-card-overlay-button {
- position: absolute;
- right: 15px;
- bottom: 15px;
- }
-
- .hub-card-image {
- width: 100%;
- height: 100%;
- object-fit: contain;
- }
-
-</style>
diff --git a/erpnext/public/js/hub/components/ItemCardsContainer.vue b/erpnext/public/js/hub/components/ItemCardsContainer.vue
deleted file mode 100644
index 0a20bcd..0000000
--- a/erpnext/public/js/hub/components/ItemCardsContainer.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<template>
- <div class="item-cards-container">
- <empty-state
- v-if="items.length === 0"
- :message="empty_state_message"
- :action="empty_state_action"
- :bordered="true"
- :height="empty_state_height"
- />
- <item-card
- v-for="item in items"
- :key="container_name + '_' +item[item_id_fieldname]"
- :item="item"
- :item_id_fieldname="item_id_fieldname"
- :is_local="is_local"
- :on_click="on_click"
- :allow_clear="editable"
- :seen="item.hasOwnProperty('seen') ? item.seen : true"
- @remove-item="$emit('remove-item', item[item_id_fieldname])"
- >
- </item-card>
- </div>
-</template>
-
-<script>
-import ItemCard from './ItemCard.vue';
-import EmptyState from './EmptyState.vue';
-
-export default {
- name: 'item-cards-container',
- props: {
- container_name: String,
- items: Array,
- item_id_fieldname: String,
- is_local: Boolean,
- on_click: Function,
- editable: Boolean,
-
- empty_state_message: String,
- empty_state_action: Object,
- empty_state_height: Number,
- empty_state_bordered: Boolean
- },
- components: {
- ItemCard,
- EmptyState
- },
- watch: {
- items() {
- // TODO: handling doesn't work
- frappe.dom.handle_broken_images($(this.$el));
- }
- }
-}
-</script>
-
-<style scoped>
- .item-cards-container {
- margin: 0 -15px;
- overflow: overlay;
- }
-</style>
diff --git a/erpnext/public/js/hub/components/ItemListCard.vue b/erpnext/public/js/hub/components/ItemListCard.vue
deleted file mode 100644
index 7f6fb77..0000000
--- a/erpnext/public/js/hub/components/ItemListCard.vue
+++ /dev/null
@@ -1,21 +0,0 @@
-<template>
- <div class="hub-list-item" :data-route="item.route">
- <div class="hub-list-left">
- <base-image class="hub-list-image" :src="item.image" />
- <div class="hub-list-body ellipsis">
- <div class="hub-list-title">{{item.item_name}}</div>
- <div class="hub-list-subtitle ellipsis">
- <slot name="subtitle"></slot>
- </div>
- </div>
- </div>
- <div class="hub-list-right" v-if="message">
- <span class="text-muted" v-html="frappe.datetime.comment_when(message.creation, true)" />
- </div>
- </div>
-</template>
-<script>
-export default {
- props: ['item', 'message']
-}
-</script>
diff --git a/erpnext/public/js/hub/components/NotificationMessage.vue b/erpnext/public/js/hub/components/NotificationMessage.vue
deleted file mode 100644
index c266726..0000000
--- a/erpnext/public/js/hub/components/NotificationMessage.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-<template>
- <div v-if="message" class="subpage-message">
- <p class="text-muted flex">
- <span v-html="message"></span>
- <i class="octicon octicon-x text-extra-muted"
- @click="$emit('remove-message')"
- >
- </i>
- </p>
- </div>
-</template>
-
-<script>
-
-export default {
- name: 'notification-message',
- props: {
- message: String,
- }
-}
-</script>
-
-<style lang="less" scoped>
- .subpage-message {
- p {
- padding: 10px 15px;
- margin-top: 0px;
- margin-bottom: 15px;
- background-color: #f9fbf7;
- border-radius: 4px;
- justify-content: space-between;
- }
-
- .octicon-x {
- cursor: pointer;
- }
- }
-</style>
diff --git a/erpnext/public/js/hub/components/Rating.vue b/erpnext/public/js/hub/components/Rating.vue
deleted file mode 100644
index 33290b8..0000000
--- a/erpnext/public/js/hub/components/Rating.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-<template>
- <span>
- <i v-for="index in max_rating"
- :key="index"
- class="fa fa-fw star-icon"
- :class="{'fa-star': index <= rating, 'fa-star-o': index > rating}"
- >
- </i>
- </span>
-</template>
-
-<script>
-export default {
- props: ['rating', 'max_rating']
-}
-</script>
diff --git a/erpnext/public/js/hub/components/ReviewArea.vue b/erpnext/public/js/hub/components/ReviewArea.vue
deleted file mode 100644
index aa83bb0..0000000
--- a/erpnext/public/js/hub/components/ReviewArea.vue
+++ /dev/null
@@ -1,140 +0,0 @@
-<template>
- <div>
- <div class="timeline-head">
- <div class="comment-input-wrapper">
- <div class="comment-input-header">
- <span class="text-muted">{{ __('Add your review') }}</span>
- <div class="btn btn-default btn-xs pull-right"
- @click="on_submit_review"
- :disabled="!(user_review.rating && user_review.subject)"
- >
- {{ __('Submit Review') }}
- </div>
- </div>
- <div class="comment-input-container">
- <div class="rating-area text-muted">
- <span>{{ __('Your rating:') }}</span>
- <div
- v-for="i in [1, 2, 3, 4, 5]"
- :key="i"
- :class="['fa fa-fw', user_review.rating < i ? 'fa-star-o' : 'fa-star']"
- :data-index="i"
- @click="set_rating(i)"
- >
- </div>
- </div>
- <div class="comment-input-body margin-top" v-show="user_review.rating">
- <input
- type="text"
- placeholder="Subject"
- class="form-control margin-bottom"
- style="border-color: #ebeff2"
- v-model="user_review.subject"
- >
- <div ref="review-content"></div>
- <div>
- <span class="text-muted text-small">{{ __('Ctrl+Enter to submit') }}</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="timeline-items">
- <review-timeline-item v-for="review in reviews"
- :key="review.user"
- :username="review.username"
- :avatar="review.user_image"
- :comment_when="when(review.modified)"
- :rating="review.rating"
- :subject="review.subject"
- :content="review.content"
- >
- </review-timeline-item>
- </div>
- </div>
-</template>
-<script>
-import ReviewTimelineItem from '../components/ReviewTimelineItem.vue';
-
-export default {
- props: ['hub_item_name'],
- data() {
- return {
- user_review: {
- rating: 0,
- subject: '',
- content: ''
- },
- reviews: []
- }
- },
- components: {
- ReviewTimelineItem
- },
- created() {
- this.get_item_reviews();
- },
- mounted() {
- this.make_input();
- },
- methods: {
- set_rating(i) {
- this.user_review.rating = i;
- },
-
- when(datetime) {
- return comment_when(datetime);
- },
-
- get_item_reviews() {
- hub.call('get_item_reviews', { hub_item_name: this.hub_item_name })
- .then(reviews => {
- this.reviews = reviews;
- })
- .catch(() => {});
- },
-
- make_input() {
- this.review_content = frappe.ui.form.make_control({
- parent: this.$refs['review-content'],
- on_submit: this.on_submit_review.bind(this),
- no_wrapper: true,
- only_input: true,
- render_input: true,
- df: {
- fieldtype: 'Comment',
- fieldname: 'comment'
- }
- });
- },
-
- on_submit_review() {
- const review = Object.assign({}, this.user_review, {
- content: this.review_content.get_value()
- });
-
- if (!hub.is_seller_registered()) {
- frappe.throw(__('You need to login as a Marketplace User before you can add any reviews.'));
- }
-
- hub.call('add_item_review', {
- hub_item_name: this.hub_item_name,
- review: JSON.stringify(review)
- })
- .then(this.push_review.bind(this));
-
- this.reset_user_review();
- },
-
- reset_user_review() {
- this.user_review.rating = 0;
- this.user_review.subject = '';
- this.review_content.set_value('');
- },
-
- push_review(review){
- this.reviews.unshift(review);
- }
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/components/ReviewTimelineItem.vue b/erpnext/public/js/hub/components/ReviewTimelineItem.vue
deleted file mode 100644
index d0e83f3..0000000
--- a/erpnext/public/js/hub/components/ReviewTimelineItem.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<template>
- <div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
- <span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px">
- <!-- ${image_html} -->
- </span>
- <div class="pull-left media-body">
- <div class="media-content-wrapper">
- <div class="action-btns">
- <!-- ${edit_html} -->
- </div>
-
- <div class="comment-header clearfix">
- <span class="pull-left avatar avatar-small visible-xs">
- <!-- ${image_html} -->
- </span>
-
- <div class="asset-details">
- <span class="author-wrap">
- <i class="octicon octicon-quote hidden-xs fa-fw"></i>
- <span>
- {{ username }}
- </span>
- </span>
- <a class="text-muted">
- <span class="text-muted hidden-xs">–</span>
- <span class="hidden-xs" v-html="comment_when"></span>
- </a>
- </div>
- </div>
- <div class="reply timeline-content-show">
- <div class="timeline-item-content">
- <p class="text-muted">
- <rating :rating="rating" :max_rating="5"></rating>
- </p>
- <h6 class="bold">{{ subject }}</h6>
- <p class="text-muted" v-html="content"></p>
- </div>
- </div>
- </div>
- </div>
- </div>
-</template>
-
-<script>
-import Rating from '../components/Rating.vue';
-
-export default {
- props: ['username', 'comment_when', 'avatar', 'rating', 'subject', 'content'],
- components: {
- Rating
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/components/SearchInput.vue b/erpnext/public/js/hub/components/SearchInput.vue
deleted file mode 100644
index 4b1ce6e..0000000
--- a/erpnext/public/js/hub/components/SearchInput.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<template>
- <div class="hub-search-container">
- <input
- type="text"
- class="form-control"
- :placeholder="placeholder"
- :value="value"
- @keydown.enter="on_input">
- </div>
-</template>
-
-<script>
-export default {
- props: {
- placeholder: String,
- value: String,
- on_search: Function
- },
- methods: {
- on_input(event) {
- this.$emit('input', event.target.value);
- this.on_search();
- }
- }
-};
-</script>
diff --git a/erpnext/public/js/hub/components/SectionHeader.vue b/erpnext/public/js/hub/components/SectionHeader.vue
deleted file mode 100644
index 05a2f83..0000000
--- a/erpnext/public/js/hub/components/SectionHeader.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-<template>
- <div class="hub-items-header level"><slot></slot></div>
-</template>
diff --git a/erpnext/public/js/hub/components/TimelineItem.vue b/erpnext/public/js/hub/components/TimelineItem.vue
deleted file mode 100644
index d13c842..0000000
--- a/erpnext/public/js/hub/components/TimelineItem.vue
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Saving this for later */
-<template>
- <div class="media timeline-item notification-content">
- <div class="small">
- <i class="octicon octicon-bookmark fa-fw"></i>
- <span title="Administrator"><b>4 weeks ago</b> Published 1 item to Marketplace</span>
- </div>
- </div>
-</template>
diff --git a/erpnext/public/js/hub/components/edit_details_dialog.js b/erpnext/public/js/hub/components/edit_details_dialog.js
deleted file mode 100644
index 97c5f83..0000000
--- a/erpnext/public/js/hub/components/edit_details_dialog.js
+++ /dev/null
@@ -1,41 +0,0 @@
-function edit_details_dialog(params) {
- let dialog = new frappe.ui.Dialog({
- title: __('Update Details'),
- fields: [
- {
- label: 'Item Name',
- fieldname: 'item_name',
- fieldtype: 'Data',
- default: params.defaults.item_name,
- reqd: 1
- },
- {
- label: 'Hub Category',
- fieldname: 'hub_category',
- fieldtype: 'Autocomplete',
- default: params.defaults.hub_category,
- options: [],
- reqd: 1
- },
- {
- label: 'Description',
- fieldname: 'description',
- fieldtype: 'Text',
- default: params.defaults.description,
- options: [],
- reqd: 1
- }
- ],
- primary_action_label: params.primary_action.label || __('Update Details'),
- primary_action: params.primary_action.fn
- });
-
- hub.call('get_categories').then(categories => {
- categories = categories.map(d => d.name);
- dialog.fields_dict.hub_category.set_data(categories);
- });
-
- return dialog;
-}
-
-export { edit_details_dialog };
diff --git a/erpnext/public/js/hub/components/item_publish_dialog.js b/erpnext/public/js/hub/components/item_publish_dialog.js
deleted file mode 100644
index 08de5b3..0000000
--- a/erpnext/public/js/hub/components/item_publish_dialog.js
+++ /dev/null
@@ -1,39 +0,0 @@
-function ItemPublishDialog(primary_action, secondary_action) {
- let dialog = new frappe.ui.Dialog({
- title: __('Edit Publishing Details'),
- fields: [
- {
- label: __('Item Code'),
- fieldname: 'item_code',
- fieldtype: 'Data',
- read_only: 1
- },
- {
- label: __('Hub Category'),
- fieldname: 'hub_category',
- fieldtype: 'Autocomplete',
- options: [],
- reqd: 1
- },
- {
- label: __('Images'),
- fieldname: 'image_list',
- fieldtype: 'MultiSelect',
- options: [],
- reqd: 1
- }
- ],
- primary_action_label: primary_action.label || __('Set Details'),
- primary_action: primary_action.fn,
- secondary_action: secondary_action.fn
- });
-
- hub.call('get_categories').then(categories => {
- categories = categories.map(d => d.name);
- dialog.fields_dict.hub_category.set_data(categories);
- });
-
- return dialog;
-}
-
-export { ItemPublishDialog };
diff --git a/erpnext/public/js/hub/components/profile_dialog.js b/erpnext/public/js/hub/components/profile_dialog.js
deleted file mode 100644
index 8e3abc3..0000000
--- a/erpnext/public/js/hub/components/profile_dialog.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const ProfileDialog = (title = __('Edit Profile'), action={}) => {
- const fields = [
- {
- fieldtype: 'Link',
- fieldname: 'company',
- label: __('Company'),
- options: 'Company'
- },
- {
- fieldtype: 'Read Only',
- fieldname: 'email',
- label: __('Email')
- },
- {
- label: __('About your company'),
- fieldname: 'company_description',
- fieldtype: 'Text'
- }
- ];
-
- let dialog = new frappe.ui.Dialog({
- title: title,
- fields: fields,
- primary_action_label: action.label || __('Update'),
- primary_action: () => {
- const form_values = dialog.get_values();
- let values_filled = true;
-
- // TODO: Say "we notice that the company description and logo isn't set. Please set them in master."
- // Only then allow to register
-
- const mandatory_fields = ['company', 'company_description'];
- mandatory_fields.forEach(field => {
- const value = form_values[field];
- if (!value) {
- dialog.set_df_property(field, 'reqd', 1);
- values_filled = false;
- }
- });
- if (!values_filled) return;
-
- action.on_submit(form_values);
- }
- });
-
- // Post create
- const default_company = frappe.defaults.get_default('company');
- dialog.set_value('company', default_company);
- dialog.set_value('email', frappe.session.user);
-
- return dialog;
-}
-
-export {
- ProfileDialog
-}
diff --git a/erpnext/public/js/hub/components/reviews.js b/erpnext/public/js/hub/components/reviews.js
deleted file mode 100644
index e34d680..0000000
--- a/erpnext/public/js/hub/components/reviews.js
+++ /dev/null
@@ -1,80 +0,0 @@
-function get_review_html(review) {
- let username = review.username || review.user || __("Anonymous");
-
- let image_html = review.user_image
- ? `<div class="avatar-frame" style="background-image: url(${review.user_image})"></div>`
- : `<div class="standard-image" style="background-color: #fafbfc">${frappe.get_abbr(username)}</div>`
-
- let edit_html = review.own
- ? `<div class="pull-right hidden-xs close-btn-container">
- <span class="small text-muted">
- ${'data.delete'}
- </span>
- </div>
- <div class="pull-right edit-btn-container">
- <span class="small text-muted">
- ${'data.edit'}
- </span>
- </div>`
- : '';
-
- let rating_html = get_rating_html(review.rating);
-
- return get_timeline_item(review, image_html, edit_html, rating_html);
-}
-
-function get_timeline_item(data, image_html, edit_html, rating_html) {
- return `<div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
- <span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px">
- ${image_html}
- </span>
- <div class="pull-left media-body">
- <div class="media-content-wrapper">
- <div class="action-btns">${edit_html}</div>
-
- <div class="comment-header clearfix">
- <span class="pull-left avatar avatar-small visible-xs">
- ${image_html}
- </span>
-
- <div class="asset-details">
- <span class="author-wrap">
- <i class="octicon octicon-quote hidden-xs fa-fw"></i>
- <span>${data.username}</span>
- </span>
- <a class="text-muted">
- <span class="text-muted hidden-xs">–</span>
- <span class="hidden-xs">${comment_when(data.modified)}</span>
- </a>
- </div>
- </div>
- <div class="reply timeline-content-show">
- <div class="timeline-item-content">
- <p class="text-muted">
- ${rating_html}
- </p>
- <h6 class="bold">${data.subject}</h6>
- <p class="text-muted">
- ${data.content}
- </p>
- </div>
- </div>
- </div>
- </div>
- </div>`;
-}
-
-function get_rating_html(rating) {
- let rating_html = ``;
- for (var i = 0; i < 5; i++) {
- let star_class = 'fa-star';
- if (i >= rating) star_class = 'fa-star-o';
- rating_html += `<i class='fa fa-fw ${star_class} star-icon' data-index=${i}></i>`;
- }
- return rating_html;
-}
-
-export {
- get_review_html,
- get_rating_html
-}
diff --git a/erpnext/public/js/hub/hub_call.js b/erpnext/public/js/hub/hub_call.js
deleted file mode 100644
index 5545a49..0000000
--- a/erpnext/public/js/hub/hub_call.js
+++ /dev/null
@@ -1,68 +0,0 @@
-frappe.provide('hub');
-frappe.provide('erpnext.hub');
-
-erpnext.hub.cache = {};
-hub.call = function call_hub_method(method, args={}, clear_cache_on_event) { // eslint-disable-line
- return new Promise((resolve, reject) => {
-
- // cache
- const key = method + JSON.stringify(args);
- if (erpnext.hub.cache[key]) {
- resolve(erpnext.hub.cache[key]);
- }
-
- // cache invalidation
- const clear_cache = () => delete erpnext.hub.cache[key];
-
- if (!clear_cache_on_event) {
- invalidate_after_5_mins(clear_cache);
- } else {
- erpnext.hub.on(clear_cache_on_event, () => {
- clear_cache(key);
- });
- }
-
- let res;
- if (hub.is_server) {
- res = frappe.call({
- method: 'hub.hub.api.' + method,
- args
- });
- } else {
- res = frappe.call({
- method: 'erpnext.hub_node.api.call_hub_method',
- args: {
- method,
- params: args
- }
- });
- }
-
- res.then(r => {
- if (r.message) {
- const response = r.message;
- if (response.error) {
- frappe.throw({
- title: __('Marketplace Error'),
- message: response.error
- });
- }
-
- erpnext.hub.cache[key] = response;
- erpnext.hub.trigger(`response:${key}`, { response });
- resolve(response);
- }
- reject(r);
-
- }).fail(reject);
- });
-};
-
-function invalidate_after_5_mins(clear_cache) {
- // cache invalidation after 5 minutes
- const timeout = 5 * 60 * 1000;
-
- setTimeout(() => {
- clear_cache();
- }, timeout);
-}
diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js
deleted file mode 100644
index 9c67c1c..0000000
--- a/erpnext/public/js/hub/hub_factory.js
+++ /dev/null
@@ -1,34 +0,0 @@
-frappe.provide('erpnext.hub');
-
-frappe.views.MarketplaceFactory = class MarketplaceFactory extends frappe.views.Factory {
- show() {
- is_marketplace_disabled()
- .then(disabled => {
- if (disabled) {
- frappe.show_not_found('Marketplace');
- return;
- }
-
- if (frappe.pages.marketplace) {
- frappe.container.change_to('marketplace');
- erpnext.hub.marketplace.refresh();
- } else {
- this.make('marketplace');
- }
- });
- }
-
- make(page_name) {
- frappe.require('marketplace.bundle.js', () => {
- erpnext.hub.marketplace = new erpnext.hub.Marketplace({
- parent: this.make_page(true, page_name)
- });
- });
- }
-};
-
-function is_marketplace_disabled() {
- return frappe.call({
- method: "erpnext.hub_node.doctype.marketplace_settings.marketplace_settings.is_marketplace_enabled"
- }).then(r => r.message)
-}
diff --git a/erpnext/public/js/hub/marketplace.bundle.js b/erpnext/public/js/hub/marketplace.bundle.js
deleted file mode 100644
index a1596e0..0000000
--- a/erpnext/public/js/hub/marketplace.bundle.js
+++ /dev/null
@@ -1,225 +0,0 @@
-import Vue from 'vue/dist/vue.js';
-import './vue-plugins';
-
-// components
-import PageContainer from './PageContainer.vue';
-import Sidebar from './Sidebar.vue';
-import { ProfileDialog } from './components/profile_dialog';
-
-// helpers
-import './hub_call';
-
-frappe.provide('hub');
-frappe.provide('erpnext.hub');
-frappe.provide('frappe.route');
-
-frappe.utils.make_event_emitter(frappe.route);
-frappe.utils.make_event_emitter(erpnext.hub);
-
-erpnext.hub.Marketplace = class Marketplace {
- constructor({ parent }) {
- this.$parent = $(parent);
- this.page = parent.page;
-
- this.update_hub_settings().then(() => {
-
- this.setup_header();
- this.make_sidebar();
- this.make_body();
- this.setup_events();
- this.refresh();
-
- if (!hub.is_server) {
- if (!hub.is_seller_registered()) {
- this.page.set_primary_action('Become a Seller', this.show_register_dialog.bind(this))
- } else {
- this.page.set_secondary_action('Add Users', this.show_add_user_dialog.bind(this));
- }
- }
- });
- }
-
- setup_header() {
- if (hub.is_server) return;
- this.page.set_title(__('Marketplace'));
- }
-
- setup_events() {
- this.$parent.on('click', '[data-route]', (e) => {
- const $target = $(e.currentTarget);
- const route = $target.data().route;
- frappe.set_route(route);
- });
-
- // generic action handler
- this.$parent.on('click', '[data-action]', e => {
- const $target = $(e.currentTarget);
- const action = $target.data().action;
-
- if (action && this[action]) {
- this[action].apply(this, $target);
- }
- })
- }
-
- make_sidebar() {
- this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs');
-
- new Vue({
- el: $('<div>').appendTo(this.$sidebar)[0],
- render: h => h(Sidebar)
- });
- }
-
- make_body() {
- this.$body = this.$parent.find('.layout-main-section');
- this.$page_container = $('<div class="hub-page-container">').appendTo(this.$body);
-
- new Vue({
- el: '.hub-page-container',
- render: h => h(PageContainer)
- });
-
- if (!hub.is_server) {
- erpnext.hub.on('seller-registered', () => {
- this.page.clear_primary_action();
- });
- }
- }
-
- refresh() {
-
- }
-
- show_register_dialog() {
- if(frappe.session.user === 'Administrator') {
- frappe.msgprint(__('You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.'));
- return;
- }
-
- if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) {
- frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to register on Marketplace.'));
- return;
- }
-
- this.register_dialog = ProfileDialog(
- __('Become a Seller'),
- {
- label: __('Register'),
- on_submit: this.register_marketplace.bind(this)
- }
- );
-
- this.register_dialog.show();
- }
-
- register_marketplace({company, company_description}) {
- frappe.call({
- method: 'erpnext.hub_node.api.register_marketplace',
- args: {
- company,
- company_description
- }
- }).then((r) => {
- if (r.message && r.message.ok) {
- this.register_dialog.hide();
-
- this.update_hub_settings()
- .then(() => {
- frappe.set_route('marketplace', 'publish');
- erpnext.hub.trigger('seller-registered');
- });
- }
- });
- }
-
- show_add_user_dialog() {
- if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) {
- frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to add users to Marketplace.'));
- return;
- }
-
- this.get_unregistered_users()
- .then(r => {
- const user_list = r.message;
-
- const d = new frappe.ui.Dialog({
- title: __('Add Users to Marketplace'),
- fields: [
- {
- label: __('Users'),
- fieldname: 'users',
- fieldtype: 'MultiSelect',
- reqd: 1,
- get_data() {
- return user_list;
- }
- }
- ],
- primary_action({ users }) {
- const selected_users = users.split(',').map(d => d.trim()).filter(Boolean);
-
- if (!selected_users.every(user => user_list.includes(user))) {
- d.set_df_property('users', 'description', __('Some emails are invalid'));
- return;
- } else {
- d.set_df_property('users', 'description', '');
- }
-
- frappe.call('erpnext.hub_node.api.register_users', {
- user_list: selected_users
- })
- .then(r => {
- d.hide();
-
- if (r.message && r.message.length) {
- frappe.show_alert(__('Added {0} users', [r.message.length]));
- }
- });
- }
- });
-
- d.show();
- });
- }
-
- get_unregistered_users() {
- return frappe.call('erpnext.hub_node.api.get_unregistered_users')
- }
-
- update_hub_settings() {
- return hub.get_settings().then(doc => {
- hub.settings = doc;
- });
- }
-}
-
-Object.assign(hub, {
- is_seller_registered() {
- return hub.settings.registered;
- },
-
- is_user_registered() {
- return this.is_seller_registered() && hub.settings.users
- .filter(hub_user => hub_user.user === frappe.session.user)
- .length === 1;
- },
-
- get_settings() {
- if (frappe.session.user === 'Guest') {
- return Promise.resolve({
- registered: 0
- });
- }
- return frappe.db.get_doc('Marketplace Settings');
- }
-});
-
-/**
- * Returns true if list_a is subset of list_b
- * @param {Array} list_a
- * @param {Array} list_b
- */
-function is_subset(list_a, list_b) {
- return list_a.every(item => list_b.includes(item));
-}
diff --git a/erpnext/public/js/hub/pages/Buying.vue b/erpnext/public/js/hub/pages/Buying.vue
deleted file mode 100644
index ebf593a..0000000
--- a/erpnext/public/js/hub/pages/Buying.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<template>
- <div>
- <section-header>
- <h4>{{ __('Buying') }}</h4>
- </section-header>
- <div class="row" v-if="items && items.length">
- <div class="col-md-7 margin-bottom"
- v-for="item of items"
- :key="item.name"
- >
- <item-list-card
- :item="item"
- v-route="'marketplace/buying/' + item.name"
- >
- <div slot="subtitle">
- <span>{{ get_sender(item.recent_message) }}: </span>
- <span>{{ item.recent_message.message | striphtml }}</span>
- </div>
- </item-list-card>
- </div>
- </div>
- <empty-state v-else :message="__('This page keeps track of items you want to buy from sellers.')" :centered="false" />
- </div>
-</template>
-<script>
-import EmptyState from '../components/EmptyState.vue';
-import SectionHeader from '../components/SectionHeader.vue';
-import ItemListCard from '../components/ItemListCard.vue';
-
-export default {
- components: {
- SectionHeader,
- ItemListCard,
- EmptyState
- },
- data() {
- return {
- items: null
- }
- },
- created() {
- this.get_items_for_messages()
- .then(items => {
- this.items = items;
- });
- },
- methods: {
- get_items_for_messages() {
- return hub.call('get_buying_items_for_messages', {}, 'action:send_message');
- },
- get_sender(message) {
- return message.sender === frappe.session.user ? __('You') : (message.sender_name || message.sender);
- }
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/pages/Category.vue b/erpnext/public/js/hub/pages/Category.vue
deleted file mode 100644
index 16d0601..0000000
--- a/erpnext/public/js/hub/pages/Category.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <search-input
- :placeholder="search_placeholder"
- :on_search="set_search_route"
- v-model="search_value"
- />
-
- <h5>{{ page_title }}</h5>
-
- <item-cards-container
- :container_name="page_title"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- :empty_state_message="empty_state_message"
- >
- </item-cards-container>
- </div>
-</template>
-
-<script>
-export default {
- data() {
- return {
- page_name: frappe.get_route()[1],
- category: frappe.get_route()[2],
- items: [],
- item_id_fieldname: 'name',
-
- // Constants
- empty_state_message: __('No items in this category yet.'),
-
- search_value: '',
-
- // Constants
- search_placeholder: __('Search for anything ...'),
-
- };
- },
- computed: {
- page_title() {
- return __(this.category);
- }
- },
- created() {
- this.search_value = '';
- this.get_items();
- },
- methods: {
- get_items() {
- hub.call('get_items', {
- filters: {
- hub_category: this.category
- }
- })
- .then((items) => {
- this.items = items;
- })
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- },
-
- set_search_route() {
- frappe.set_route('marketplace', 'search', this.category, this.search_value);
- },
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue
deleted file mode 100644
index 8380b2b..0000000
--- a/erpnext/public/js/hub/pages/FeaturedItems.vue
+++ /dev/null
@@ -1,116 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <h5>{{ page_title }}</h5>
- <p v-if="items.length"
- class="text-muted margin-bottom">
- {{ __('You can Feature upto 8 items.') }}
- </p>
-
- <item-cards-container
- :container_name="page_title"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- :editable="true"
- @remove-item="on_item_remove"
- :empty_state_message="empty_state_message"
- >
- </item-cards-container>
- </div>
-</template>
-
-<script>
-export default {
- name: 'featured-items-page',
- data() {
- return {
- page_name: frappe.get_route()[1],
- items: [],
- item_id_fieldname: 'name',
-
- // Constants
- page_title: __('Your Featured Items'),
- empty_state_message: __('No featured items yet. Got to your {0} and feature up to eight items that you want to highlight to your customers.',
- [`<a href="#marketplace/published-items">${__("Published Items")}</a>`])
- };
- },
- created() {
- this.get_items();
- },
- methods: {
- get_items() {
- hub.call(
- 'get_featured_items_of_seller', {},
- 'action:item_feature'
- )
- .then((items) => {
- this.items = items;
- })
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- },
-
- on_item_remove(hub_item_name) {
- const grace_period = 5000;
- let reverted = false;
- let alert;
-
- const undo_remove = () => {
- this.toggle_item(hub_item_name);;
- reverted = true;
- alert.hide();
- return false;
- }
-
- const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
-
- alert_message = __('{0} removed. {1}', [item_name,
- `<a href="#" data-action="undo-remove"><b>${__('Undo')}</b></a>`]);
- alert = frappe.show_alert(alert_message, grace_period / 1000,
- {
- 'undo-remove': undo_remove.bind(this)
- }
- );
-
- this.toggle_item(hub_item_name, false);
-
- setTimeout(() => {
- if(!reverted) {
- this.remove_item_from_featured_items(hub_item_name);
- }
- }, grace_period);
- },
-
- remove_item_from_featured_items(hub_item_name) {
- erpnext.hub.trigger('action:item_feature');
- hub.call('remove_item_from_seller_featured_items', {
- hub_item_name,
- hub_user: frappe.session.user
- })
- .then(() => {
- this.get_items();
- })
- .catch(e => {
- console.log(e);
- });
- },
-
- // By default show
- toggle_item(hub_item_name, show=true) {
- this.items = this.items.map(item => {
- if(item.name === hub_item_name) {
- item.seen = show;
- }
- return item;
- });
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Home.vue b/erpnext/public/js/hub/pages/Home.vue
deleted file mode 100644
index 8fe8245..0000000
--- a/erpnext/public/js/hub/pages/Home.vue
+++ /dev/null
@@ -1,114 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <search-input
- :placeholder="search_placeholder"
- :on_search="set_search_route"
- v-model="search_value"
- />
-
- <div v-if="show_skeleton">
- <section-header>
- <h4 class="hub-skeleton">Explore Explore Explore</h4>
- </section-header>
- <div class="row">
- <div class="col-md-3 col-sm-4 col-xs-6 hub-card-container" v-for="(f, $index) in [1, 2, 3, 4, 5, 6, 7]" :key="$index">
- <div class="hub-skeleton" style="height: 262px; width: 100%; margin-bottom: 25px;"></div>
- </div>
- </div>
- </div>
-
- <div v-else v-for="section in sections" :key="section.title">
-
- <section-header>
- <h4>{{ section.title }}</h4>
- <p v-if="section.expandable" :data-route="'marketplace/category/' + section.title">{{ 'See All' }}</p>
- </section-header>
-
- <item-cards-container
- :container_name="section.title"
- :items="section.items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- />
- </div>
- </div>
-</template>
-
-<script>
-export default {
- name: 'home-page',
- data() {
- return {
- page_name: frappe.get_route()[1],
- item_id_fieldname: 'name',
- search_value: '',
-
- sections: [],
- show_skeleton: true,
-
- // Constants
- search_placeholder: __('Search for anything ...'),
- };
- },
- created() {
- // refreshed
- this.search_value = '';
- this.get_items();
- },
- mounted() {
- frappe.route.on('change', () => {
- if (frappe.get_route_str() === 'marketplace/home') {
- this.get_items();
- }
- })
- },
- methods: {
- get_items() {
- hub.call('get_data_for_homepage', frappe.defaults ? {
- country: frappe.defaults.get_user_default('country')
- } : null)
- .then((data) => {
- this.show_skeleton = false;
-
- this.sections.push({
- title: __('Explore'),
- items: data.random_items
- });
- if (data.items_by_country.length) {
- this.sections.push({
- title: __('Near you'),
- items: data.items_by_country
- });
- }
-
- const category_items = data.category_items;
-
- if (category_items) {
- Object.keys(category_items).map(category => {
- const items = category_items[category];
-
- this.sections.push({
- title: __(category),
- expandable: true,
- items
- });
- });
- }
- })
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- },
-
- set_search_route() {
- frappe.set_route('marketplace', 'search', 'All', this.search_value);
- },
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Item.vue b/erpnext/public/js/hub/pages/Item.vue
deleted file mode 100644
index 93002a7..0000000
--- a/erpnext/public/js/hub/pages/Item.vue
+++ /dev/null
@@ -1,356 +0,0 @@
-<template>
- <div class="marketplace-page" :data-page-name="page_name" v-if="init || item">
- <detail-view
- :title="title"
- :image="image"
- :sections="sections"
- :menu_items="menu_items"
- :show_skeleton="init"
- >
- <detail-header-item slot="detail-header-item" :value="item_subtitle"></detail-header-item>
- <detail-header-item slot="detail-header-item" :value="item_views_and_ratings"></detail-header-item>
-
- <button
- v-if="primary_action"
- slot="detail-header-item"
- class="btn btn-primary btn-sm margin-top"
- @click="primary_action.action"
- >{{ primary_action.label }}</button>
- </detail-view>
-
- <review-area v-if="!init" :hub_item_name="hub_item_name"></review-area>
- </div>
-</template>
-
-<script>
-import ReviewArea from '../components/ReviewArea.vue';
-import { get_rating_html } from '../components/reviews';
-import { edit_details_dialog } from '../components/edit_details_dialog';
-
-export default {
- name: 'item-page',
- components: {
- ReviewArea
- },
- data() {
- return {
- page_name: frappe.get_route()[1],
- hub_item_name: frappe.get_route()[2],
-
- init: true,
-
- item: null,
- title: null,
- image: null,
- sections: []
- };
- },
- computed: {
- is_own_item() {
- let is_own_item = false;
- if (this.item) {
- if (this.item.hub_seller === hub.settings.hub_seller_name) {
- is_own_item = true;
- }
- }
- return is_own_item;
- },
- menu_items() {
- return [
- {
- label: __('Save Item'),
- condition: hub.is_user_registered() && !this.is_own_item,
- action: this.add_to_saved_items
- },
- {
- label: __('Add to Featured Item'),
- condition: hub.is_user_registered() && this.is_own_item,
- action: this.add_to_featured_items
- },
- {
- label: __('Report this Item'),
- condition: !this.is_own_item,
- action: this.report_item
- },
- {
- label: __('Edit Details'),
- condition: hub.is_user_registered() && this.is_own_item,
- action: this.edit_details
- },
- {
- label: __('Unpublish Item'),
- condition: hub.is_user_registered() && this.is_own_item,
- action: this.unpublish_item
- }
- ];
- },
-
- item_subtitle() {
- if (!this.item) {
- return '';
- }
-
- const dot_spacer = '<span aria-hidden="true"> · </span>';
- let subtitle_items = [comment_when(this.item.creation)];
- const rating = this.item.average_rating;
-
- if (rating > 0) {
- subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`);
- }
-
- subtitle_items.push({
- value: this.item.company,
- on_click: this.go_to_seller_profile_page
- });
-
- return subtitle_items;
- },
-
- item_views_and_ratings() {
- if (!this.item) {
- return '';
- }
-
- let stats = __('No views yet');
- if (this.item.view_count) {
- const views_message = __('{0} Views', [this.item.view_count]);
-
- const rating_html = get_rating_html(this.item.average_rating);
- const rating_count =
- this.item.no_of_ratings > 0
- ? __('{0} reviews', [this.item.no_of_ratings])
- : __('No reviews yet');
-
- stats = [views_message, rating_html, rating_count];
- }
-
- return stats;
- },
-
- primary_action() {
- if (hub.is_user_registered()) {
- return {
- label: __('Contact Seller'),
- action: this.contact_seller.bind(this)
- };
- } else {
- return undefined;
- }
- }
- },
- created() {
- this.get_item_details();
- },
- mounted() {
- // To record a single view per session, (later)
- // erpnext.hub.item_view_cache = erpnext.hub.item_view_cache || [];
- // if (erpnext.hub.item_view_cache.includes(this.hub_item_name)) {
- // return;
- // }
-
- this.item_received.then(() => {
- setTimeout(() => {
- hub.call('add_item_view', {
- hub_item_name: this.hub_item_name
- });
- // .then(() => {
- // erpnext.hub.item_view_cache.push(this.hub_item_name);
- // });
- }, 5000);
- });
- },
- methods: {
- get_item_details() {
- this.item_received = hub
- .call('get_item_details', { hub_item_name: this.hub_item_name })
- .then(item => {
- this.init = false;
- this.item = item;
-
- this.build_data();
- this.make_dialogs();
- });
- },
- go_to_seller_profile_page(seller_name) {
- frappe.set_route(`marketplace/seller/${seller_name}`);
- },
- build_data() {
- this.title = this.item.item_name || this.item.name;
- this.image = this.item.image;
-
- this.sections = [
- {
- title: __('Item Description'),
- content: this.item.description
- ? __(this.item.description)
- : __('No description')
- },
- {
- title: __('Seller Information'),
- content: this.item.seller_description
- ? __(this.item.seller_description)
- : __('No description')
- }
- ];
- },
-
- make_dialogs() {
- this.make_contact_seller_dialog();
- this.make_report_item_dialog();
- this.make_editing_dialog();
- },
-
- add_to_saved_items() {
- hub.call('add_item_to_user_saved_items', {
- hub_item_name: this.hub_item_name,
- hub_user: frappe.session.user
- })
- .then(() => {
- const saved_items_link = `<b><a href="#marketplace/saved-items">${__('Saved')}</a></b>`;
- frappe.show_alert(saved_items_link);
- erpnext.hub.trigger('action:item_save');
- })
- .catch(e => {
- console.error(e);
- });
- },
-
- add_to_featured_items() {
- hub.call('add_item_to_seller_featured_items', {
- hub_item_name: this.hub_item_name,
- hub_user: frappe.session.user
- })
- .then(() => {
- const featured_items_link = `<b><a href="#marketplace/featured-items">${__('Added to Featured Items')}</a></b>`;
- frappe.show_alert(featured_items_link);
- erpnext.hub.trigger('action:item_feature');
- })
- .catch(e => {
- console.error(e);
- });
- },
-
- make_contact_seller_dialog() {
- this.contact_seller_dialog = new frappe.ui.Dialog({
- title: __('Send a message'),
- fields: [
- {
- fieldname: 'to',
- fieldtype: 'Read Only',
- label: __('To'),
- default: this.item.company
- },
- {
- fieldtype: 'Text',
- fieldname: 'message',
- label: __('Message')
- }
- ],
- primary_action: ({ message }) => {
- if (!message) return;
-
- hub.call('send_message', {
- hub_item: this.item.name,
- message
- })
- .then(() => {
- this.contact_seller_dialog.hide();
- frappe.set_route('marketplace', 'buying', this.item.name);
- erpnext.hub.trigger('action:send_message');
- });
- }
- });
- },
-
- make_report_item_dialog() {
- this.report_item_dialog = new frappe.ui.Dialog({
- title: __('Report Item'),
- fields: [
- {
- label: __('Why do think this Item should be removed?'),
- fieldtype: 'Text',
- fieldname: 'message'
- }
- ],
- primary_action: ({ message }) => {
- hub.call('add_reported_item', {
- hub_item_name: this.item.name,
- message
- })
- .then(() => {
- d.hide();
- frappe.show_alert(__('Item Reported'));
- });
- }
- });
- },
-
- make_editing_dialog() {
- this.edit_dialog = edit_details_dialog({
- primary_action: {
- fn: values => {
- this.update_details(values);
- this.edit_dialog.hide();
- }
- },
- defaults: {
- item_name: this.item.item_name,
- hub_category: this.item.hub_category,
- description: this.item.description
- }
- });
- },
-
- update_details(values) {
- frappe.call('erpnext.hub_node.api.update_item', {
- ref_doc: this.item.name,
- data: values
- })
- .then(r => {
- return this.get_item_details();
- })
- .then(() => {
- frappe.show_alert(__('{0} Updated', [this.item.item_name]));
- });
- },
-
- contact_seller() {
- this.contact_seller_dialog.show();
- },
-
- report_item() {
- if (!hub.is_seller_registered()) {
- frappe.throw(
- __('Please login as a Marketplace User to report this item.')
- );
- }
- this.report_item_dialog.show();
- },
-
- edit_details() {
- if (!hub.is_seller_registered()) {
- frappe.throw(
- __('Please login as a Marketplace User to edit this item.')
- );
- }
- this.edit_dialog.show();
- },
-
- unpublish_item() {
- frappe.confirm(__('Unpublish {0}?', [this.item.item_name]), () => {
- frappe
- .call('erpnext.hub_node.api.unpublish_item', {
- item_code: this.item.item_code,
- hub_item_name: this.hub_item_name
- })
- .then(r => {
- frappe.set_route(`marketplace/home`);
- frappe.show_alert(__('Item listing removed'));
- });
- });
- }
- }
-};
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Messages.vue b/erpnext/public/js/hub/pages/Messages.vue
deleted file mode 100644
index 73506e9..0000000
--- a/erpnext/public/js/hub/pages/Messages.vue
+++ /dev/null
@@ -1,104 +0,0 @@
-<template>
- <div v-if="item_details">
- <div>
- <a class="text-muted" v-route="back_link">← {{ __('Back to Messages') }}</a>
- </div>
- <section-header>
- <div class="flex flex-column margin-bottom">
- <h4>{{ item_details.item_name }}</h4>
- <span class="text-muted">{{ item_details.company }}</span>
- </div>
- </section-header>
- <div class="row">
- <div class="col-md-7">
- <div class="message-container">
- <div class="message-list">
- <div class="level margin-bottom" v-for="message in messages" :key="message.name">
- <div class="level-left ellipsis" style="width: 80%;">
- <div v-html="frappe.avatar(message.sender)" />
- <div style="white-space: normal;" v-html="message.message" />
- </div>
- <div class="level-right text-muted" v-html="frappe.datetime.comment_when(message.creation, true)" />
- </div>
- </div>
- <div class="message-input">
- <comment-input @change="send_message" />
- </div>
- </div>
- </div>
- </div>
- </div>
-</template>
-<script>
-import CommentInput from '../components/CommentInput.vue';
-import ItemListCard from '../components/ItemListCard.vue';
-
-export default {
- components: {
- CommentInput,
- ItemListCard
- },
- data() {
- return {
- message_type: frappe.get_route()[1],
- item_details: null,
- messages: []
- }
- },
- created() {
- const hub_item_name = this.get_hub_item_name();
- this.get_item_details(hub_item_name)
- .then(item_details => {
- this.item_details = item_details;
- this.get_messages()
- .then(messages => {
- this.messages = messages;
- });
- });
- },
- computed: {
- back_link() {
- return 'marketplace/' + this.message_type;
- }
- },
- methods: {
- send_message(message) {
- this.messages.push({
- sender: frappe.session.user,
- message: message,
- creation: Date.now(),
- name: frappe.utils.get_random(6)
- });
- hub.call('send_message', {
- to_seller: this.get_against_seller(),
- hub_item: this.item_details.name,
- message
- });
- },
- get_item_details(hub_item_name) {
- return hub.call('get_item_details', { hub_item_name })
- },
- get_messages() {
- if (!this.item_details) return [];
- return hub.call('get_messages', {
- against_seller: this.get_against_seller(),
- against_item: this.item_details.name
- });
- },
- get_against_seller() {
- if (this.message_type === 'buying') {
- return this.item_details.hub_seller;
- } else if (this.message_type === 'selling') {
- return frappe.get_route()[2];
- }
- },
- get_hub_item_name() {
- if (this.message_type === 'buying') {
- return frappe.get_route()[2];
- } else if (this.message_type === 'selling') {
- return frappe.get_route()[3];
- }
- }
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/pages/NotFound.vue b/erpnext/public/js/hub/pages/NotFound.vue
deleted file mode 100644
index 8901b97..0000000
--- a/erpnext/public/js/hub/pages/NotFound.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <empty-state
- :message="empty_state_message"
- :height="500"
- :action="action"
- >
- </empty-state>
-
- </div>
-</template>
-
-<script>
-export default {
- name: 'not-found-page',
- data() {
- return {
- page_name: 'not-found',
- action: {
- label: __('Back to Home'),
- on_click: () => {
- frappe.set_route(`marketplace/home`);
- }
- },
-
- // Constants
- empty_state_message: __('Sorry! We could not find what you were looking for.')
- };
- },
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Publish.vue b/erpnext/public/js/hub/pages/Publish.vue
deleted file mode 100644
index ecba4b1..0000000
--- a/erpnext/public/js/hub/pages/Publish.vue
+++ /dev/null
@@ -1,212 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <notification-message
- v-if="last_sync_message"
- :message="last_sync_message"
- @remove-message="clear_last_sync_message"
- ></notification-message>
-
- <div class="flex justify-between align-flex-end margin-bottom">
- <h5>{{ page_title }}</h5>
-
- <button class="btn btn-primary btn-sm publish-items"
- :disabled="no_selected_items"
- @click="publish_selected_items"
- >
- <span>{{ publish_button_text }}</span>
- </button>
- </div>
-
- <item-cards-container
- :container_name="page_title"
- :items="selected_items"
- :item_id_fieldname="item_id_fieldname"
- :is_local="true"
- :editable="true"
- @remove-item="remove_item_from_selection"
-
- :empty_state_message="empty_state_message"
- :empty_state_bordered="true"
- :empty_state_height="80"
- >
- </item-cards-container>
-
- <p class="text-muted">{{ valid_items_instruction }}</p>
-
- <search-input
- :placeholder="search_placeholder"
- :on_search="get_valid_items"
- v-model="search_value"
- >
- </search-input>
-
- <item-cards-container
- :items="valid_items"
- :item_id_fieldname="item_id_fieldname"
- :is_local="true"
- :on_click="show_publishing_dialog_for_item"
- >
- </item-cards-container>
- </div>
-</template>
-
-<script>
-import NotificationMessage from '../components/NotificationMessage.vue';
-import { ItemPublishDialog } from '../components/item_publish_dialog';
-
-export default {
- name: 'publish-page',
- components: {
- NotificationMessage
- },
- data() {
- return {
- page_name: frappe.get_route()[1],
- valid_items: [],
- selected_items: [],
- items_data_to_publish: {},
- search_value: '',
- item_id_fieldname: 'item_code',
-
- // Constants
- // TODO: multiline translations don't work
- page_title: __('Publish Items'),
- search_placeholder: __('Search Items ...'),
- empty_state_message: __('No Items selected yet. Browse and click on items below to publish.'),
- valid_items_instruction: __('Only items with an image and description can be published. Please update them if an item in your inventory does not appear.'),
- last_sync_message: (hub.settings.last_sync_datetime)
- ? __('Last sync was {0}.', [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]) +
- ` <a href="#marketplace/published-items">${__('See your Published Items.')}</a>`
- : ''
- };
- },
- computed: {
- no_selected_items() {
- return this.selected_items.length === 0;
- },
-
- publish_button_text() {
- const number = this.selected_items.length;
- let text = __('Publish');
- if(number === 1) {
- text = __('Publish 1 Item');
- }
- if(number > 1) {
- text = __('Publish {0} Items', [number]);
- }
- return text;
- },
-
- items_dict() {
- let items_dict = {};
- this.valid_items.map(item => {
- items_dict[item[this.item_id_fieldname]] = item
- })
-
- return items_dict;
- },
- },
- created() {
- this.get_valid_items();
- this.make_publishing_dialog();
- },
- methods: {
- get_valid_items() {
- frappe.call(
- 'erpnext.hub_node.api.get_valid_items',
- {
- search_value: this.search_value
- }
- )
- .then((r) => {
- this.valid_items = r.message;
- })
- },
-
- publish_selected_items() {
- frappe.call(
- 'erpnext.hub_node.api.publish_selected_items',
- {
- items_to_publish: this.selected_items
- }
- )
- .then((r) => {
- this.selected_items = [];
- return frappe.db.get_doc('Marketplace Settings');
- })
- .then(doc => {
- hub.settings = doc;
- this.add_last_sync_message();
- });
- },
-
- add_last_sync_message() {
- this.last_sync_message = __('Last sync was {0}.',
- [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]
- ) + `<a href="#marketplace/published-items">${__('See your Published Items')}</a>.`;
- },
-
- clear_last_sync_message() {
- this.last_sync_message = '';
- },
-
- remove_item_from_selection(item_code) {
- this.selected_items = this.selected_items
- .filter(item => item.item_code !== item_code);
- },
-
- make_publishing_dialog() {
- this.item_publish_dialog = ItemPublishDialog(
- {
- fn: (values) => {
- this.add_item_to_publish(values);
- this.item_publish_dialog.hide();
- }
- },
- {
- fn: () => {
- const values = this.item_publish_dialog.get_values(true);
- this.update_items_data_to_publish(values);
- }
- }
- );
- },
-
- add_item_to_publish(values) {
- this.update_items_data_to_publish(values);
-
- const item_code = values.item_code;
- let item_doc = this.items_dict[item_code];
-
- const item_to_publish = Object.assign({}, item_doc, values);
- this.selected_items.push(item_to_publish);
- },
-
- update_items_data_to_publish(values) {
- this.items_data_to_publish[values.item_code] = values;
- },
-
- show_publishing_dialog_for_item(item_code) {
- let item_data = this.items_data_to_publish[item_code];
- if(!item_data) { item_data = { item_code }; };
-
- this.item_publish_dialog.clear();
-
- const item_doc = this.items_dict[item_code];
- if(item_doc) {
- this.item_publish_dialog.fields_dict.image_list.set_data(
- item_doc.attachments.map(attachment => attachment.file_url)
- );
- }
-
- this.item_publish_dialog.set_values(item_data);
- this.item_publish_dialog.show();
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/PublishedItems.vue b/erpnext/public/js/hub/pages/PublishedItems.vue
deleted file mode 100644
index cbb2216..0000000
--- a/erpnext/public/js/hub/pages/PublishedItems.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <section-header>
- <div>
- <h5>{{ __('Published Items') }}</h5>
- <p v-if="items.length"
- class="text-muted margin-bottom">
- {{ __('You can publish upto 200 items.') }}
- </p>
- </div>
-
- <button v-if="items.length"
- class="btn btn-default btn-xs publish-items"
- v-route="'marketplace/publish'"
- >
- <span>{{ __('Publish More Items') }}</span>
- </button>
-
- </section-header>
-
- <item-cards-container
- :container_name="__('Published Items')"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- :empty_state_message="__('You haven\'t published any items yet.')"
- :empty_state_action="publish_page_action"
- >
- </item-cards-container>
- </div>
-</template>
-
-<script>
-export default {
- data() {
- return {
- page_name: frappe.get_route()[1],
- items: [],
- item_id_fieldname: 'name',
-
- publish_page_action: {
- label: __('Publish Your First Items'),
- on_click: () => {
- frappe.set_route(`marketplace/publish`);
- }
- }
- };
- },
- created() {
- this.get_items();
- },
- methods: {
- get_items() {
- hub.call('get_items', {
- filters: {
- hub_seller: hub.settings.hub_seller_name
- }
- })
- .then((items) => {
- this.items = items;
- })
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/SavedItems.vue b/erpnext/public/js/hub/pages/SavedItems.vue
deleted file mode 100644
index 7007ddc..0000000
--- a/erpnext/public/js/hub/pages/SavedItems.vue
+++ /dev/null
@@ -1,116 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <h5>{{ page_title }}</h5>
-
- <item-cards-container
- :container_name="page_title"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- :editable="true"
- @remove-item="on_item_remove"
- :empty_state_message="empty_state_message"
- >
- </item-cards-container>
- </div>
-</template>
-
-<script>
-export default {
- name: 'saved-items-page',
- data() {
- return {
- page_name: frappe.get_route()[1],
- items: [],
- item_id_fieldname: 'name',
-
- // Constants
- page_title: __('Saved Items'),
- empty_state_message: __('You have not saved any items yet.')
- };
- },
- created() {
- this.get_items();
- },
- methods: {
- get_items() {
- hub.call(
- 'get_saved_items_of_user', {},
- 'action:item_save'
- )
- .then((items) => {
- this.items = items;
- })
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- },
-
- on_item_remove(hub_item_name) {
- const grace_period = 5000;
- let reverted = false;
- let alert;
-
- const undo_remove = () => {
- this.toggle_item(hub_item_name);;
- reverted = true;
- alert.hide();
- return false;
- }
-
- const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
-
- alert = frappe.show_alert(`
- <span>
- ${__('{0} removed.', [item_name], 'A specific Item has been removed.')}
- <a href="#" data-action="undo-remove">
- <b>${__('Undo', None, 'Undo removal of item.')}</b>
- </a>
- </span>`,
- grace_period/1000,
- {
- 'undo-remove': undo_remove.bind(this)
- }
- );
-
- this.toggle_item(hub_item_name, false);
-
- setTimeout(() => {
- if(!reverted) {
- this.remove_item_from_saved_items(hub_item_name);
- }
- }, grace_period);
- },
-
- remove_item_from_saved_items(hub_item_name) {
- erpnext.hub.trigger('action:item_save');
- hub.call('remove_item_from_user_saved_items', {
- hub_item_name,
- hub_user: frappe.session.user
- })
- .then(() => {
- this.get_items();
- })
- .catch(e => {
- console.log(e);
- });
- },
-
- // By default show
- toggle_item(hub_item_name, show=true) {
- this.items = this.items.map(item => {
- if(item.name === hub_item_name) {
- item.seen = show;
- }
- return item;
- });
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Search.vue b/erpnext/public/js/hub/pages/Search.vue
deleted file mode 100644
index c10841e..0000000
--- a/erpnext/public/js/hub/pages/Search.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- >
- <search-input
- :placeholder="search_placeholder"
- :on_search="set_route_and_get_items"
- v-model="search_value"
- >
- </search-input>
-
- <h5>{{ page_title }}</h5>
-
- <item-cards-container
- container_name="Search"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- :empty_state_message="empty_state_message"
- >
- </item-cards-container>
- </div>
-</template>
-
-<script>
-export default {
- data() {
- return {
- page_name: frappe.get_route()[1],
- items: [],
- category: frappe.get_route()[2],
- search_value: frappe.get_route()[3],
- item_id_fieldname: 'name',
- filters: {},
-
- // Constants
- search_placeholder: __('Search for anything ...'),
- empty_state_message: __('')
- };
- },
- computed: {
- page_title() {
- return this.items.length
- ? __('Results for "{0}" {1}', [
- this.search_value,
- this.category !== 'All' ? __('in category {0}', [this.category]) : ''
- ])
- : __('No Items found.');
- }
- },
- created() {
- this.get_items();
- },
- methods: {
- get_items() {
- if (this.category !== 'All') {
- this.filters['hub_category'] = this.category;
- }
- hub.call('get_items', {
- keyword: this.search_value,
- filters: this.filters
- })
- .then((items) => {
- this.items = items;
- })
- },
-
- set_route_and_get_items() {
- frappe.set_route('marketplace', 'search', this.category, this.search_value);
- this.get_items();
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue
deleted file mode 100644
index 3c9b800..0000000
--- a/erpnext/public/js/hub/pages/Seller.vue
+++ /dev/null
@@ -1,201 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- v-if="init || profile"
- >
- <detail-view
- :title="title"
- :image="image"
- :sections="sections"
- :show_skeleton="init"
- >
- <detail-header-item slot="detail-header-item"
- :value="country"
- ></detail-header-item>
- <detail-header-item slot="detail-header-item"
- :value="site_name"
- ></detail-header-item>
- <detail-header-item slot="detail-header-item"
- :value="joined_when"
- ></detail-header-item>
-
- </detail-view>
-
- <div v-if="items.length">
- <h5>
- {{ item_container_heading }}
- <small v-if="is_user_registered() && is_own_company">
- <a class="pull-right" href="#marketplace/featured-items">Customize your Featured Items</a>
- </small>
- </h5>
- <item-cards-container
- :container_name="item_container_heading"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- >
- </item-cards-container>
- <a class="pull-right" @click="go_to_seller_items_page(seller_company)">Show all items</a>
- </div>
-
- <div v-if="recent_seller_reviews.length">
- <h5>Customer Reviews</h5>
- <div class="container" v-for="review in recent_seller_reviews" :key="review.name">
- <br>
- <span class="text-muted">
- <rating :rating="review.rating" :max_rating="5"></rating>
- </span>
- <i class="octicon octicon-quote hidden-xs fa-fw"></i>
- <span class="bold">{{ review.subject }}</span>
- <i class="octicon octicon-quote hidden-xs fa-fw fa-rotate-180"></i>
- <div class="container">
- by {{ review.username }}
- <a class="text-muted">
- <span class="text-muted hidden-xs">–</span>
- <span class="hidden-xs" v-html="comment_when(review.timestamp)"></span>
- </a>
- </div>
- </div>
- </div>
-
- <div v-if="seller_product_view_stats.length">
- <h5>Stats</h5>
- <div id="seller_traffic_chart"></div>
- </div>
-
-
-
- </div>
-</template>
-
-<script>
-import Rating from '../components/Rating.vue';
-
-
-export default {
- name: 'seller-page',
- components: {
- Rating
- },
- data() {
- return {
- page_name: frappe.get_route()[1],
- seller_company: frappe.get_route()[2],
- hub_seller: null,
-
- init: true,
-
- profile: null,
- items:[],
- recent_seller_reviews: [],
- seller_product_view_stats: [],
- seller_traffic_chart: null,
- item_id_fieldname: 'name',
- item_container_heading: 'Items',
-
- title: null,
- image: null,
- sections: [],
-
- country: '',
- site_name: '',
- joined_when: '',
- };
- },
- created() {
- this.get_seller_profile_and_items();
- },
- computed: {
- is_own_company() {
- let is_own_company = false;
- if(this.hub_seller) {
- if(this.hub_seller === hub.settings.hub_seller_name) {
- is_own_company = true;
- }
- }
- return is_own_company;
- },
- },
- methods: {
- comment_when(timestamp){
- return comment_when(timestamp)
- },
- is_user_registered(){
- return hub.is_user_registered()
- },
- get_seller_profile_and_items() {
- let post_data = {company: this.seller_company}
- if (this.page_name == 'profile'){
- this.seller_company = null;
- this.hub_seller = hub.settings.hub_seller_name
- post_data = {hub_seller: this.hub_seller}
- }
- hub.call('get_hub_seller_page_info', post_data)
- .then(data => {
- this.init = false;
- this.profile = data.profile;
- this.items = data.items;
- this.item_container_heading = data.is_featured_item ? __('Featured Items') : __('Popular Items');
- this.hub_seller = this.items[0].hub_seller;
- this.recent_seller_reviews = data.recent_seller_reviews;
- this.seller_product_view_stats = data.seller_product_view_stats;
-
- const profile = this.profile;
-
- this.title = profile.company;
-
- this.country = __(profile.country);
- this.site_name = __(profile.site_name);
- this.joined_when = __('Joined {0}', [comment_when(profile.creation)]);
-
- this.image = profile.logo;
- this.sections = [
- {
- title: __('About the Company'),
- content: profile.company_description
- ? __(profile.company_description)
- : __('No description')
- }
- ];
-
- setTimeout(() => this.init_seller_traffic_chart(), 1);
-
- });
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- },
- go_to_seller_items_page(hub_seller) {
- frappe.set_route(`marketplace/seller/${hub_seller}/items`);
- },
- init_seller_traffic_chart() {
- let lables = []
- let tooltip_lables = {}
- let datasets = [{name:"Product Views",chartType: 'line',values: []}]
- this.seller_product_view_stats.map((stat) => {
- lables.push(stat.date.substring(5));
- tooltip_lables[stat.date.substring(5)] = new Date(stat.date).toDateString();
- datasets[0].values.push(stat.view_count);
- });
- let data = {labels: lables, datasets:datasets, tooltip_lables:tooltip_lables}
- this.seller_traffic_chart = new Chart( "#seller_traffic_chart", { // or DOM element
- data: data,
-
- title: "Daily Product Views",
- type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage'
- height: 300,
- colors: ['purple', '#ffa3ef', 'light-blue'],
-
- tooltipOptions: {
- formatTooltipX: d => this.seller_traffic_chart.data.tooltip_lables[d],
- formatTooltipY: d => d + ' Views',
- }
- });
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/SellerItems.vue b/erpnext/public/js/hub/pages/SellerItems.vue
deleted file mode 100644
index 852fbae..0000000
--- a/erpnext/public/js/hub/pages/SellerItems.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- v-if="init || items.length"
- >
- <h5>{{ item_container_heading }}</h5>
- <item-cards-container
- :container_name="item_container_heading"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- >
- </item-cards-container>
- </div>
-</template>
-
-<script>
-export default {
- name: 'seller-items-page',
- data() {
- return {
- page_name: frappe.get_route()[1],
- seller_company: frappe.get_route()[2],
-
- init: true,
- items:[],
- item_id_fieldname: 'name',
- };
- },
- created() {
- this.get_seller_and_items();
- },
- computed: {
- item_container_heading() {
- return __('Items by ' + this.seller_company);
- }
- },
- methods: {
- get_seller_and_items() {
- hub.call(
- 'get_items',
- { company: this.seller_company }
- ).then(data => {
- this.init = false;
- this.items = data;
- });
- },
-
- go_to_item_details_page(hub_item_name) {
- frappe.set_route(`marketplace/item/${hub_item_name}`);
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Selling.vue b/erpnext/public/js/hub/pages/Selling.vue
deleted file mode 100644
index 8743027..0000000
--- a/erpnext/public/js/hub/pages/Selling.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<template>
- <div>
- <section-header>
- <h4>{{ __('Selling') }}</h4>
- </section-header>
- <div class="row" v-if="items && items.length">
- <div class="col-md-7"
- style="margin-bottom: 30px;"
- v-for="item of items"
- :key="item.name"
- >
- <item-list-card
- :item="item"
- >
- <div slot="subtitle">
- <span class="text-muted">{{ __('{0} conversations', [item.received_messages.length]) }}</span>
- </div>
- </item-list-card>
- <div class="hub-list-item" v-for="(message, index) in item.received_messages" :key="index"
- v-route="'marketplace/selling/' + message.buyer + '/' + item.name"
- >
- <div class="hub-list-left">
- <div class="hub-list-body">
- <div class="hub-list-title">
- {{ message.buyer_name }}
- </div>
- <div class="hub-list-subtitle">
- {{ message.sender }}: {{ message.message | striphtml }}
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <empty-state v-else :message="__('This page keeps track of your items in which buyers have showed some interest.')" :centered="false" />
- </div>
-</template>
-<script>
-import EmptyState from '../components/EmptyState.vue';
-import SectionHeader from '../components/SectionHeader.vue';
-import ItemListCard from '../components/ItemListCard.vue';
-
-export default {
- components: {
- SectionHeader,
- ItemListCard,
- EmptyState
- },
- data() {
- return {
- items: null
- }
- },
- created() {
- this.get_items_for_messages()
- .then(items => {
- this.items = items;
- });
- },
- methods: {
- get_items_for_messages() {
- return hub.call('get_selling_items_for_messages');
- }
- }
-}
-</script>
diff --git a/erpnext/public/js/hub/vue-plugins.js b/erpnext/public/js/hub/vue-plugins.js
deleted file mode 100644
index 4912d68..0000000
--- a/erpnext/public/js/hub/vue-plugins.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import Vue from 'vue/dist/vue.js';
-
-// Global components
-import ItemCardsContainer from './components/ItemCardsContainer.vue';
-import SectionHeader from './components/SectionHeader.vue';
-import SearchInput from './components/SearchInput.vue';
-import DetailView from './components/DetailView.vue';
-import DetailHeaderItem from './components/DetailHeaderItem.vue';
-import EmptyState from './components/EmptyState.vue';
-import Image from './components/Image.vue';
-
-Vue.prototype.__ = window.__;
-Vue.prototype.frappe = window.frappe;
-
-Vue.component('item-cards-container', ItemCardsContainer);
-Vue.component('section-header', SectionHeader);
-Vue.component('search-input', SearchInput);
-Vue.component('detail-view', DetailView);
-Vue.component('detail-header-item', DetailHeaderItem);
-Vue.component('empty-state', EmptyState);
-Vue.component('base-image', Image);
-
-Vue.directive('route', {
- bind(el, binding) {
- const route = binding.value;
- if (!route) return;
- el.classList.add('cursor-pointer');
- el.dataset.route = route;
- el.addEventListener('click', () => frappe.set_route(route));
- },
- unbind(el) {
- el.classList.remove('cursor-pointer');
- }
-});
-
-const handleImage = (el, src) => {
- let img = new Image();
- // add loading class
- el.src = '';
- el.classList.add('img-loading');
-
- img.onload = () => {
- // image loaded, remove loading class
- el.classList.remove('img-loading');
- // set src
- el.src = src;
- }
- img.onerror = () => {
- el.classList.remove('img-loading');
- el.classList.add('no-image');
- el.src = null;
- }
- img.src = src;
-}
-
-Vue.filter('striphtml', function (text) {
- return strip_html(text || '');
-});
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 9caf1de..cad1659 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -84,11 +84,15 @@
});
},
+ route_to_pending_reposts: (args) => {
+ frappe.set_route('List', 'Repost Item Valuation', args);
+ },
+
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',
+ method: 'erpnext.hr.doctype.hr_settings.hr_settings.set_proceed_with_frequency_change',
callback: () => {
cur_frm.save();
}
@@ -709,14 +713,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();
},
@@ -744,9 +755,13 @@
}
frappe.form.link_formatters['Employee'] = function(value, doc) {
- if(doc && doc.employee_name && doc.employee_name !== value) {
- return value? value + ': ' + doc.employee_name: doc.employee_name;
+ if (doc && value && doc.employee_name && doc.employee_name !== value && doc.employee === value) {
+ return value + ': ' + doc.employee_name;
+ } else if (!value && doc.doctype && doc.employee_name) {
+ // format blank value in child table
+ return doc.employee;
} else {
+ // if value is blank in report view or project name and name are the same, return as is
return value;
}
}
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/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss
index 1677e9b..7a3854c 100644
--- a/erpnext/public/scss/point-of-sale.scss
+++ b/erpnext/public/scss/point-of-sale.scss
@@ -495,6 +495,11 @@
font-size: var(--text-md);
}
+ > .item-qty-total-container {
+ @extend .net-total-container;
+ padding: 5px 0px 0px 0px;
+ }
+
> .taxes-container {
display: none;
flex-direction: column;
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
index 490a7c4..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 {
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.py b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
index d4e8cc7..a2198f3 100644
--- a/erpnext/quality_management/doctype/non_conformance/non_conformance.py
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..3e94b35 100644
--- a/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
+++ b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
@@ -1,10 +1,9 @@
-# -*- 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 TestNonConformance(unittest.TestCase):
pass
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.py b/erpnext/quality_management/doctype/quality_action/quality_action.py
index 02401ba..87245f9 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.py
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and contributors
# 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'
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 98d665f..fefa9df 100644
--- a/erpnext/quality_management/doctype/quality_action/test_quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/test_quality_action.py
@@ -1,11 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQualityAction(unittest.TestCase):
# quality action has no code
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..7ede3e4de 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 d3e96cf..ec5d67f 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 QualityFeedback(Document):
@frappe.whitelist()
def set_parameters(self):
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..fe36cc6 100644
--- a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
class TestQualityFeedback(unittest.TestCase):
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..ff2c841 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..4590f9d 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 afed14b..4b8bc0f 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQualityFeedbackTemplate(unittest.TestCase):
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..13e215f 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 3e616b7..22ba810 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and contributors
# 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
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 0e135b5..67fdaca 100644
--- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
-from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
+
class TestQualityGoal(unittest.TestCase):
def test_quality_goal(self):
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..eaa8db2 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 9e453eb..481b3c1 100644
--- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py
+++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py
@@ -1,9 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class QualityMeeting(Document):
pass
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 6bf4c17..910b8a1 100644
--- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
+++ b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
@@ -1,11 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQualityMeeting(unittest.TestCase):
# nothing to test
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..c2f5b3f 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..8b09f6d 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..f6998df 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 QualityMeetingMinutes(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
index 117db00..0f535ba 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and contributors
# 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'
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 4fa7734..6130895 100644
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and Contributors
# 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:
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..a03c871 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 QualityProcedureProcess(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py
index 34cc890..b896f8d 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and contributors
# 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
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 161ecd0..8a254db 100644
--- a/erpnext/quality_management/doctype/quality_review/test_quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
@@ -1,14 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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()
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..462a971 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 QualityReviewObjective(Document):
pass
diff --git a/erpnext/quality_management/workspace/quality/quality.json b/erpnext/quality_management/workspace/quality/quality.json
index 4dc8129..ae28470 100644
--- a/erpnext/quality_management/workspace/quality/quality.json
+++ b/erpnext/quality_management/workspace/quality/quality.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Goal\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Procedure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Inspection\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Review\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Action\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Conformance\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goal and Procedure\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Feedback\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Meeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Review and Action\", \"col\": 4}}]",
"creation": "2020-03-02 15:49:28.632014",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "quality",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Quality",
"links": [
{
@@ -149,15 +142,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:01.699912",
+ "modified": "2021-08-05 12:16:01.699913",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/regional/__init__.py b/erpnext/regional/__init__.py
index faa5912..c460286 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -1,11 +1,13 @@
# Copyright (c) 2018, Frappe Technologies and contributors
# 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 +30,4 @@
"document_name": doc.name,
"data": data
}).insert(ignore_permissions=True)
+
diff --git a/erpnext/regional/address_template/test_regional_address_template.py b/erpnext/regional/address_template/test_regional_address_template.py
index 8a05ea2..780db40 100644
--- a/erpnext/regional/address_template/test_regional_address_template.py
+++ b/erpnext/regional/address_template/test_regional_address_template.py
@@ -1,9 +1,9 @@
-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..686a93e 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.py
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..ba70eb4 100644
--- a/erpnext/regional/doctype/datev_settings/test_datev_settings.py
+++ b/erpnext/regional/doctype/datev_settings/test_datev_settings.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestDATEVSettings(unittest.TestCase):
pass
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 4791dc2..3b73a5c 100644
--- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
+++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class GSTHSNCode(Document):
pass
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..6dbca1a 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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.json b/erpnext/regional/doctype/gst_settings/gst_settings.json
index 95b930c..fc579d4 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.json
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.json
@@ -6,8 +6,10 @@
"engine": "InnoDB",
"field_order": [
"gst_summary",
- "column_break_2",
+ "gst_tax_settings_section",
"round_off_gst_values",
+ "column_break_4",
+ "hsn_wise_tax_breakup",
"gstin_email_sent_on",
"section_break_4",
"gst_accounts",
@@ -17,37 +19,23 @@
{
"fieldname": "gst_summary",
"fieldtype": "HTML",
- "label": "GST Summary",
- "show_days": 1,
- "show_seconds": 1
- },
- {
- "fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "label": "GST Summary"
},
{
"fieldname": "gstin_email_sent_on",
"fieldtype": "Date",
"label": "GSTIN Email Sent On",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "section_break_4",
- "fieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Section Break"
},
{
"fieldname": "gst_accounts",
"fieldtype": "Table",
"label": "GST Accounts",
- "options": "GST Account",
- "show_days": 1,
- "show_seconds": 1
+ "options": "GST Account"
},
{
"default": "250000",
@@ -56,24 +44,35 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "B2C Limit",
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"default": "0",
"description": "Enabling this option will round off individual GST components in all the Invoices",
"fieldname": "round_off_gst_values",
"fieldtype": "Check",
- "label": "Round Off GST Values",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Round Off GST Values"
+ },
+ {
+ "default": "0",
+ "fieldname": "hsn_wise_tax_breakup",
+ "fieldtype": "Check",
+ "label": "Tax Breakup Table Based On HSN Code"
+ },
+ {
+ "fieldname": "gst_tax_settings_section",
+ "fieldtype": "Section Break",
+ "label": "GST Tax Settings"
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-01-28 17:19:47.969260",
+ "modified": "2021-10-11 18:10:14.242614",
"modified_by": "Administrator",
"module": "Regional",
"name": "GST Settings",
@@ -83,4 +82,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.py b/erpnext/regional/doctype/gst_settings/gst_settings.py
index af3d92e..13ef3e0 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.py
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.py
@@ -1,13 +1,15 @@
-# -*- 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, 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..5c7d2b4 100644
--- a/erpnext/regional/doctype/gst_settings/test_gst_settings.py
+++ b/erpnext/regional/doctype/gst_settings/test_gst_settings.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestGSTSettings(unittest.TestCase):
pass
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..d48cd67 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -1,17 +1,18 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 erpnext.regional.india import state_numbers
+
class GSTR3BReport(Document):
def validate(self):
self.get_data()
@@ -279,7 +280,7 @@
if self.get('invoice_items'):
# Build itemised tax for export invoices, nil and exempted where tax table is blank
- for invoice, items in iteritems(self.invoice_items):
+ for invoice, items in self.invoice_items.items():
if invoice not in self.items_based_on_tax_rate and self.invoice_detail_map.get(invoice, {}).get('export_type') \
== "Without Payment of Tax" and self.invoice_detail_map.get(invoice, {}).get('gst_category') == "Overseas":
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
@@ -347,7 +348,7 @@
self.report_dict['sup_details']['isup_rev']['txval'] += taxable_value
def set_inter_state_supply(self, inter_state_supply):
- for key, value in iteritems(inter_state_supply):
+ for key, value in inter_state_supply.items():
if key[0] == "Unregistered":
self.report_dict["inter_sup"]["unreg_details"].append(value)
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..e12e3d7 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
@@ -1,15 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import json
+import unittest
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.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.stock.doctype.item.test_item import make_item
-import json
test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
@@ -103,6 +103,45 @@
gst_settings.round_off_gst_values = 1
gst_settings.save()
+ def test_gst_category_auto_update(self):
+ if not frappe.db.exists("Customer", "_Test GST Customer With GSTIN"):
+ customer = frappe.get_doc({
+ "customer_group": "_Test Customer Group",
+ "customer_name": "_Test GST Customer With GSTIN",
+ "customer_type": "Individual",
+ "doctype": "Customer",
+ "territory": "_Test Territory"
+ }).insert()
+
+ self.assertEqual(customer.gst_category, 'Unregistered')
+
+ if not frappe.db.exists('Address', '_Test GST Category-1-Billing'):
+ address = frappe.get_doc({
+ "address_line1": "_Test Address Line 1",
+ "address_title": "_Test GST Category-1",
+ "address_type": "Billing",
+ "city": "_Test City",
+ "state": "Test State",
+ "country": "India",
+ "doctype": "Address",
+ "is_primary_address": 1,
+ "phone": "+91 0000000000",
+ "gstin": "29AZWPS7135H1ZG",
+ "gst_state": "Karnataka",
+ "gst_state_number": "29"
+ }).insert()
+
+ address.append("links", {
+ "link_doctype": "Customer",
+ "link_name": "_Test GST Customer With GSTIN"
+ })
+
+ address.save()
+
+ customer.load_from_db()
+ self.assertEqual(customer.gst_category, 'Registered Regular')
+
+
def make_sales_invoice():
si = create_sales_invoice(company="_Test Company GST",
customer = '_Test GST Customer',
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..97b8488 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
+++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
@@ -1,23 +1,22 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-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..78c07c5 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
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestImportSupplierInvoice(unittest.TestCase):
pass
diff --git a/erpnext/healthcare/doctype/body_part/__init__.py b/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/body_part/__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/healthcare/doctype/body_part/__init__.py b/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
similarity index 100%
copy from erpnext/healthcare/doctype/body_part/__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/buying/doctype/supplier_item_group/__init__.py b/erpnext/regional/doctype/ksa_vat_setting/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__init__.py
copy to erpnext/regional/doctype/ksa_vat_setting/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
new file mode 100644
index 0000000..00b62b9
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Havenir Solutions and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('KSA VAT Setting', {
+ onload: function () {
+ frappe.breadcrumbs.add('Accounts', 'KSA VAT Setting');
+ }
+});
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
new file mode 100644
index 0000000..3361946
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "autoname": "field:company",
+ "creation": "2021-07-13 08:49:01.100356",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "ksa_vat_sales_accounts",
+ "ksa_vat_purchase_accounts"
+ ],
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "ksa_vat_sales_accounts",
+ "fieldtype": "Table",
+ "label": "KSA VAT Sales Accounts",
+ "options": "KSA VAT Sales Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "ksa_vat_purchase_accounts",
+ "fieldtype": "Table",
+ "label": "KSA VAT Purchase Accounts",
+ "options": "KSA VAT Purchase Account",
+ "reqd": 1
+ }
+ ],
+ "links": [],
+ "modified": "2021-08-26 04:29:06.499378",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Setting",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "company",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
new file mode 100644
index 0000000..bdae116
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATSetting(Document):
+ pass
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
new file mode 100644
index 0000000..269cbec
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
@@ -0,0 +1,5 @@
+frappe.listview_settings['KSA VAT Setting'] = {
+ onload () {
+ frappe.breadcrumbs.add('Accounts');
+ }
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
new file mode 100644
index 0000000..7207901
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestKSAVATSetting(unittest.TestCase):
+ pass
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
index f48fe6f..c32ab6b 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
@@ -7,7 +7,7 @@
"engine": "InnoDB",
"field_order": [
"certificate_details_section",
- "section_code",
+ "tax_withholding_category",
"fiscal_year",
"column_break_3",
"certificate_no",
@@ -34,13 +34,6 @@
"unique": 1
},
{
- "fieldname": "section_code",
- "fieldtype": "Select",
- "label": "Section Code",
- "options": "192\n193\n194\n194A\n194C\n194D\n194H\n194I\n194J\n194LA\n194LBB\n194LBC\n195",
- "reqd": 1
- },
- {
"fieldname": "section_break_3",
"fieldtype": "Section Break",
"label": "Deductee Details"
@@ -123,13 +116,22 @@
"label": "Fiscal Year",
"options": "Fiscal Year",
"reqd": 1
+ },
+ {
+ "fieldname": "tax_withholding_category",
+ "fieldtype": "Link",
+ "label": "Tax Withholding Category",
+ "options": "Tax Withholding Category",
+ "reqd": 1
}
],
+ "index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-04-23 23:04:41.203721",
+ "modified": "2021-10-23 18:33:38.962622",
"modified_by": "Administrator",
"module": "Regional",
"name": "Lower Deduction Certificate",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index 656c329..f148881 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -1,18 +1,19 @@
-# -*- 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.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()
+ self.validate_supplier_against_tax_category()
def validate_dates(self):
if getdate(self.valid_upto) < getdate(self.valid_from):
@@ -28,12 +29,14 @@
<= fiscal_year.year_end_date):
frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
- def validate_supplier_against_section_code(self):
- duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', {'supplier': self.supplier, 'section_code': self.section_code}, ['name', 'valid_from', 'valid_upto'], as_dict=True)
+ def validate_supplier_against_tax_category(self):
+ duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate',
+ {'supplier': self.supplier, 'tax_withholding_category': self.tax_withholding_category, 'name': ("!=", self.name)},
+ ['name', 'valid_from', 'valid_upto'], as_dict=True)
if duplicate_certificate and self.are_dates_overlapping(duplicate_certificate):
certificate_link = get_link_to_form('Lower Deduction Certificate', duplicate_certificate.name)
- frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against Section Code {2} for this time period.")
- .format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.section_code)))
+ frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against category {2} for this time period.")
+ .format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.tax_withholding_category)))
def are_dates_overlapping(self,duplicate_certificate):
valid_from = duplicate_certificate.valid_from
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py
index 7e95020..d8e7801 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
@@ -1,10 +1,9 @@
-# -*- 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 TestLowerDeductionCertificate(unittest.TestCase):
pass
diff --git a/erpnext/healthcare/page/patient_history/__init__.py b/erpnext/regional/doctype/product_tax_category/__init__.py
similarity index 100%
rename from erpnext/healthcare/page/patient_history/__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..0f08978 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
@@ -1,14 +1,15 @@
-# -*- 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 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):
@@ -81,7 +82,6 @@
memberships = frappe.db.get_all('Membership', {
'member': self.member,
'from_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
- 'to_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
'membership_status': ('!=', 'Cancelled')
}, ['from_date', 'amount', 'name', 'invoice', 'payment_id'], order_by='from_date')
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 41b4203..6fa3b85 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
@@ -1,16 +1,21 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
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):
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..bb7f07f 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
@@ -1,10 +1,10 @@
-# -*- 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 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..f2fc34d 100644
--- a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
+++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..464c2e6 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
@@ -1,10 +1,9 @@
-# -*- 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 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..1af32e4 100644
--- a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class UAEVATSettings(Document):
pass
diff --git a/erpnext/regional/france/setup.py b/erpnext/regional/france/setup.py
index db6419e..5d48203 100644
--- a/erpnext/regional/france/setup.py
+++ b/erpnext/regional/france/setup.py
@@ -1,7 +1,6 @@
# 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.custom.doctype.custom_field.custom_field import create_custom_fields
diff --git a/erpnext/regional/france/utils.py b/erpnext/regional/france/utils.py
index 424615d..8413165 100644
--- a/erpnext/regional/france/utils.py
+++ b/erpnext/regional/france/utils.py
@@ -1,8 +1,7 @@
# Copyright (c) 2018, Frappe Technologies and contributors
# 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 be3d7a3..8f2dc2d 100644
--- a/erpnext/regional/germany/utils/datev/datev_constants.py
+++ b/erpnext/regional/germany/utils/datev/datev_constants.py
@@ -1,4 +1,3 @@
-# coding: utf-8
"""Constants used in datev.py."""
TRANSACTION_COLUMNS = [
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
index 122c15f..2d1e02e 100644
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -1,15 +1,12 @@
-# coding: utf-8
-from __future__ import unicode_literals
-
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,12 +30,20 @@
if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS:
result['Belegdatum'] = pd.to_datetime(result['Belegdatum'])
+ result['Beleginfo - Inhalt 6'] = pd.to_datetime(result['Beleginfo - Inhalt 6'])
+ result['Beleginfo - Inhalt 6'] = result['Beleginfo - Inhalt 6'].dt.strftime('%d%m%Y')
+
+ result['Fälligkeit'] = pd.to_datetime(result['Fälligkeit'])
+ result['Fälligkeit'] = result['Fälligkeit'].dt.strftime('%d%m%y')
+
+ result.sort_values(by='Belegdatum', inplace=True, kind='stable', ignore_index=True)
+
if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES:
result['Sprach-ID'] = 'de-DE'
data = result.to_csv(
# Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035
- sep=str(';'),
+ sep=';',
# European decimal seperator
decimal=',',
# Windows "ANSI" encoding
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index faeb36f..c703575 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-from six import iteritems
states = [
'',
@@ -82,4 +80,4 @@
"West Bengal": "19",
}
-number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
+number_state_mapping = {v: k for k, v in state_numbers.items()}
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
deleted file mode 100644
index 765b51f..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 4db5551..5865424 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -1,7 +1,6 @@
# 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, os, json
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
@@ -13,7 +12,7 @@
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': 'India'}) <=1:
+ if patch or frappe.db.count('Company', {'country': 'India'}) <=1:
setup_company_independent_fixtures(patch=patch)
if not patch:
@@ -132,6 +131,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)
@@ -165,12 +168,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 = [
@@ -181,18 +184,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')
]
@@ -230,25 +233,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' "),
@@ -354,7 +357,8 @@
'insert_after': 'transporter',
'fetch_from': 'transporter.gst_transporter_id',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 20
},
{
'fieldname': 'driver',
@@ -370,7 +374,8 @@
'fieldtype': 'Data',
'insert_after': 'driver',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 30
},
{
'fieldname': 'vehicle_no',
@@ -378,7 +383,8 @@
'fieldtype': 'Data',
'insert_after': 'lr_no',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 10
},
{
'fieldname': 'distance',
@@ -395,7 +401,7 @@
{
'fieldname': 'transporter_name',
'label': 'Transporter Name',
- 'fieldtype': 'Data',
+ 'fieldtype': 'Small Text',
'insert_after': 'transporter_col_break',
'fetch_from': 'transporter.name',
'read_only': 1,
@@ -409,12 +415,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,
@@ -437,7 +444,8 @@
'default': 'Regular',
'insert_after': 'lr_date',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 30
},
{
'fieldname': 'ewaybill',
@@ -446,10 +454,31 @@
'depends_on': 'eval:(doc.docstatus === 1)',
'allow_on_submit': 1,
'insert_after': 'tax_id',
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 20
}
]
+ 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 = {
'Address': [
dict(fieldname='gstin', label='Party GSTIN', fieldtype='Data',
@@ -463,7 +492,9 @@
'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,
+ '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,
@@ -480,6 +511,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],
@@ -582,10 +614,16 @@
],
'Supplier': [
{
+ 'fieldname': 'pan',
+ 'label': 'PAN',
+ 'fieldtype': 'Data',
+ 'insert_after': 'supplier_type'
+ },
+ {
'fieldname': 'gst_transporter_id',
'label': 'GST Transporter ID',
'fieldtype': 'Data',
- 'insert_after': 'supplier_type',
+ 'insert_after': 'pan',
'depends_on': 'eval:doc.is_transporter'
},
{
@@ -608,10 +646,16 @@
],
'Customer': [
{
+ 'fieldname': 'pan',
+ 'label': 'PAN',
+ 'fieldtype': 'Data',
+ 'insert_after': 'customer_type'
+ },
+ {
'fieldname': 'gst_category',
'label': 'GST Category',
'fieldtype': 'Select',
- 'insert_after': 'customer_type',
+ 'insert_after': 'pan',
'options': 'Registered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
'default': 'Unregistered'
},
@@ -640,9 +684,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 = []
@@ -729,7 +783,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')
@@ -737,11 +791,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)
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")):
@@ -756,9 +810,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])
@@ -781,149 +836,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/test_utils.py b/erpnext/regional/india/test_utils.py
index a16f56c..c95a0b3 100644
--- a/erpnext/regional/india/test_utils.py
+++ b/erpnext/regional/india/test_utils.py
@@ -1,8 +1,8 @@
-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 ce5aa10..fd3ec3c 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -1,19 +1,16 @@
-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 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}$")
@@ -29,12 +26,13 @@
gst_category = []
- if len(doc.links):
- link_doctype = doc.links[0].get("link_doctype")
- link_name = doc.links[0].get("link_name")
+ if hasattr(doc, 'gst_category'):
+ if len(doc.links):
+ link_doctype = doc.links[0].get("link_doctype")
+ link_name = doc.links[0].get("link_name")
- if link_doctype in ["Customer", "Supplier"]:
- gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category'])
+ if link_doctype in ["Customer", "Supplier"]:
+ gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category'])
doc.gstin = doc.gstin.upper().strip()
if not doc.gstin or doc.gstin == 'NA':
@@ -62,7 +60,7 @@
.format(doc.gst_state_number), title=_("Invalid GSTIN"))
def validate_pan_for_india(doc, method):
- if doc.get('country') != 'India' or not doc.pan:
+ if doc.get('country') != 'India' or not doc.get('pan'):
return
if not PAN_NUMBER_FORMAT.match(doc.pan):
@@ -78,10 +76,9 @@
def update_gst_category(doc, method):
for link in doc.links:
if link.link_doctype in ['Customer', 'Supplier']:
- if doc.get('gstin'):
- frappe.db.sql("""
- UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered'
- """.format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec
+ meta = frappe.get_meta(link.link_doctype)
+ if doc.get('gstin') and meta.has_field('gst_category'):
+ frappe.db.set_value(link.link_doctype, {'name': link.link_name, 'gst_category': 'Unregistered'}, 'gst_category', 'Registered Regular')
def set_gst_state_and_state_number(doc):
if not doc.gst_state:
@@ -112,12 +109,13 @@
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'):
+ hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
+ if frappe.get_meta(item_doctype).has_field('gst_hsn_code') and hsn_wise_in_gst_settings:
return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
else:
return [_("Item"), _("Taxable Amount")] + tax_accounts
-def get_itemised_tax_breakup_data(doc, account_wise=False):
+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 +123,32 @@
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"))
+ hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
+
+ tax_breakup_hsn_wise = hsn_wise or hsn_wise_in_gst_settings
+ if tax_breakup_hsn_wise:
+ item_hsn_map = frappe._dict()
+ for d in doc.items:
+ item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
hsn_tax = {}
for item, taxes in itemised_tax.items():
- hsn_code = item_hsn_map.get(item)
- hsn_tax.setdefault(hsn_code, frappe._dict())
+ item_or_hsn = item if not tax_breakup_hsn_wise else item_hsn_map.get(item)
+ hsn_tax.setdefault(item_or_hsn, frappe._dict())
for tax_desc, tax_detail in taxes.items():
key = tax_desc
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 tax_breakup_hsn_wise else item_hsn_map.get(item)
+ hsn_taxable_amount.setdefault(item_or_hsn, 0)
+ hsn_taxable_amount[item_or_hsn] += itemised_taxable_amount.get(item)
return hsn_tax, hsn_taxable_amount
@@ -189,7 +191,7 @@
@frappe.whitelist()
def get_regional_address_details(party_details, doctype, company):
- if isinstance(party_details, string_types):
+ if isinstance(party_details, str):
party_details = json.loads(party_details)
party_details = frappe._dict(party_details)
@@ -204,26 +206,17 @@
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
master_doctype = "Sales Taxes and Charges Template"
- get_tax_template_based_on_category(master_doctype, company, party_details)
-
- if party_details.get('taxes_and_charges'):
- return party_details
-
- if not party_details.company_gstin:
- return party_details
+ tax_template_by_category = get_tax_template_based_on_category(master_doctype, company, party_details)
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
master_doctype = "Purchase Taxes and Charges Template"
- get_tax_template_based_on_category(master_doctype, company, party_details)
+ tax_template_by_category = get_tax_template_based_on_category(master_doctype, company, party_details)
- if party_details.get('taxes_and_charges'):
- return party_details
-
- if not party_details.supplier_gstin:
- return party_details
+ if tax_template_by_category:
+ party_details['taxes_and_charges'] = tax_template_by_category
+ return
if not party_details.place_of_supply: return party_details
-
if not party_details.company_gstin: return party_details
if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin
@@ -235,6 +228,7 @@
if not default_tax:
return party_details
+
party_details["taxes_and_charges"] = default_tax
party_details.taxes = get_taxes_and_charges(master_doctype, default_tax)
@@ -251,6 +245,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:
@@ -263,9 +260,7 @@
default_tax = frappe.db.get_value(master_doctype, {'company': company, 'tax_category': party_details.get('tax_category')},
'name')
- if default_tax:
- party_details["taxes_and_charges"] = default_tax
- party_details.taxes = get_taxes_and_charges(master_doctype, default_tax)
+ return default_tax
def get_tax_template(master_doctype, company, is_inter_state, state_code):
tax_categories = frappe.get_all('Tax Category', fields = ['name', 'is_inter_state', 'gst_state'],
@@ -438,9 +433,9 @@
data = get_address_details(data, doc, company_address, billing_address, dispatch_address)
data.itemList = []
- data.totalValue = doc.total
+ data.totalValue = doc.net_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
@@ -551,7 +546,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,18 +558,18 @@
'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)
- for hsn_code, taxable_amount in hsn_taxable_amount.items():
+ hsn_wise_charges, hsn_taxable_amount = get_itemised_tax_breakup_data(doc, account_wise=True, hsn_wise=hsn_wise)
+ for item_or_hsn, taxable_amount in hsn_taxable_amount.items():
item_data = frappe._dict()
- if not hsn_code:
+ if not item_or_hsn:
frappe.throw(_('GST HSN Code does not exist for one or more items'))
- item_data.hsnCode = int(hsn_code)
+ item_data.hsnCode = int(item_or_hsn) if hsn_wise else item_or_hsn
item_data.taxableAmount = taxable_amount
item_data.qtyUnit = ""
for attr in item_data_attrs:
item_data[attr] = 0
- for account, tax_detail in hsn_wise_charges.get(hsn_code, {}).items():
+ for account, tax_detail in hsn_wise_charges.get(item_or_hsn, {}).items():
account_type = gst_accounts.get(account, '')
for tax_acc, attrs in tax_map.items():
if account_type == tax_acc:
@@ -767,6 +762,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')
@@ -774,7 +778,7 @@
if country != 'India':
return
- if isinstance(account_list, string_types):
+ if isinstance(account_list, str):
account_list = json.loads(account_list)
if not frappe.db.get_single_value('GST Settings', 'round_off_gst_values'):
@@ -833,13 +837,11 @@
doc.get('items')[item_count - 1].taxable_value += diff
def get_depreciation_amount(asset, depreciable_value, row):
- depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
-
if row.depreciation_method in ("Straight Line", "Manual"):
# if the Depreciation Schedule is being prepared for the first time
if not asset.flags.increase_in_asset_life:
- depreciation_amount = (flt(row.value_after_depreciation) -
- flt(row.expected_value_after_useful_life)) / depreciation_left
+ depreciation_amount = (flt(asset.gross_purchase_amount) -
+ flt(row.expected_value_after_useful_life)) / flt(row.total_number_of_depreciations)
# if the Depreciation Schedule is being modified after Asset Repair
else:
@@ -850,12 +852,13 @@
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))
@@ -887,4 +890,3 @@
gst_settings.remove(d)
gst_settings.save()
-
diff --git a/erpnext/regional/italy/__init__.py b/erpnext/regional/italy/__init__.py
index 4932f66..eb20f65 100644
--- a/erpnext/regional/italy/__init__.py
+++ b/erpnext/regional/italy/__init__.py
@@ -1,5 +1,3 @@
-# coding=utf-8
-
fiscal_regimes = [
"RF01-Ordinario",
"RF02-Contribuenti minimi (art.1, c.96-117, L. 244/07)",
diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py
index 7db2f6b..531f10d 100644
--- a/erpnext/regional/italy/setup.py
+++ b/erpnext/regional/italy/setup.py
@@ -2,7 +2,6 @@
# License: GNU General Public License v3. See license.txt
# coding=utf-8
-from __future__ import unicode_literals
import frappe
from frappe import _
diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py
index 56f609e..c82557b 100644
--- a/erpnext/regional/italy/utils.py
+++ b/erpnext/regional/italy/utils.py
@@ -1,13 +1,12 @@
-from __future__ import unicode_literals
-
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.utils import cstr, flt
from frappe.utils.file_manager import remove_file
-from six import string_types
+
+from erpnext.controllers.taxes_and_totals import get_itemised_tax
from erpnext.regional.italy import state_codes
@@ -166,7 +165,7 @@
if tax.rate == 0:
for item in items:
item_tax_rate = item.item_tax_rate
- if isinstance(item.item_tax_rate, string_types):
+ if isinstance(item.item_tax_rate, str):
item_tax_rate = json.loads(item.item_tax_rate)
if item_tax_rate and tax.account_head in item_tax_rate:
diff --git a/erpnext/healthcare/__init__.py b/erpnext/regional/print_format/ksa_pos_invoice/__init__.py
similarity index 100%
copy from erpnext/healthcare/__init__.py
copy to erpnext/regional/print_format/ksa_pos_invoice/__init__.py
diff --git a/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json b/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json
new file mode 100644
index 0000000..c2a3092
--- /dev/null
+++ b/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json
@@ -0,0 +1,32 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2021-12-07 13:25:05.424827",
+ "css": "",
+ "custom_format": 1,
+ "default_print_language": "en",
+ "disabled": 1,
+ "doc_type": "POS Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font_size": 0,
+ "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tline-height: 150%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n<p class=\"text-center\" style=\"margin-bottom: 1rem\">\n\t{{ doc.company }}<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n\t<img src={{doc.ksa_einv_qr}}>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Cashier\") }}:</b> {{ doc.owner }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Time\") }}:</b> {{ doc.get_formatted(\"posting_time\") }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"35%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"SR.No\") }}:</b><br>\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"net_amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.change_amount -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
+ "idx": 0,
+ "line_breaks": 0,
+ "margin_bottom": 0.0,
+ "margin_left": 0.0,
+ "margin_right": 0.0,
+ "margin_top": 0.0,
+ "modified": "2021-12-08 10:25:01.930885",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA POS Invoice",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 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/__init__.py b/erpnext/regional/print_format/ksa_vat_invoice/__init__.py
similarity index 100%
copy from erpnext/healthcare/__init__.py
copy to erpnext/regional/print_format/ksa_vat_invoice/__init__.py
diff --git a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json
new file mode 100644
index 0000000..6b64d47
--- /dev/null
+++ b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json
@@ -0,0 +1,32 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2021-10-29 22:46:26.039023",
+ "css": ".qr-code{\n float:right;\n}\n\n.invoice-heading {\n margin: 0;\n}\n\n.ksa-invoice-table {\n border: 1px solid #888a8e;\n border-collapse: collapse;\n width: 100%;\n margin: 20px 0;\n font-size: 16px;\n}\n\n.ksa-invoice-table.two-columns td:nth-child(2) {\n direction: rtl;\n}\n\n.ksa-invoice-table th {\n border: 1px solid #888a8e;\n max-width: 50%;\n padding: 8px;\n}\n\n.ksa-invoice-table td {\n padding: 5px;\n border: 1px solid #888a8e;\n max-width: 50%;\n}\n\n.ksa-invoice-table thead,\n.ksa-invoice-table tfoot {\n text-transform: uppercase;\n}\n\n.qr-rtl {\n direction: rtl;\n}\n\n.qr-flex{\n display: flex;\n justify-content: space-between;\n}",
+ "custom_format": 1,
+ "default_print_language": "en",
+ "disabled": 1,
+ "doc_type": "Sales Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font_size": 14,
+ "html": "<div class=\"ksa-vat-format\">\n <div class=\"qr-flex\">\n <div style=\"qr-flex: 1\">\n <h2 class=\"invoice-heading\">TAX INVOICE</h2>\n <h2 class=\"invoice-heading\">\u0641\u0627\u062a\u0648\u0631\u0629 \u0636\u0631\u064a\u0628\u064a\u0629</h2>\n </div>\n \n <img class=\"qr-code\" src={{doc.ksa_einv_qr}}>\n </div>\n {% set company = frappe.get_doc(\"Company\", doc.company)%}\n {% if (doc.company_address) %}\n {% set supplier_address_doc = frappe.get_doc('Address', doc.company_address) %}\n {% endif %}\n \n {% if(doc.customer_address) %}\n {% set customer_address = frappe.get_doc('Address', doc.customer_address ) %}\n {% endif %}\n \n {% if(doc.shipping_address_name) %}\n {% set customer_shipping_address = frappe.get_doc('Address', doc.shipping_address_name ) %}\n {% endif %} \n \n <table class=\"ksa-invoice-table two-columns\">\n <thead>\n <tr>\n <th>{{ company.name }}</th>\n <th style=\"text-align: right;\">{{ company.company_name_in_arabic }}</th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Invoice Info -->\n <tr>\n <td>Invoice#: {{doc.name}}</td>\n <td>\u0631\u0642\u0645 \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.name}}</td>\n </tr>\n <tr>\n <td>Invoice Date: {{doc.posting_date}}</td>\n <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.posting_date}}</td>\n </tr>\n <tr>\n <td>Date of Supply:{{doc.posting_date}}</td>\n <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0648\u0631\u064a\u062f: {{doc.posting_date}}</td>\n </tr>\n \n <!--Supplier Info -->\n <tr>\n <td>Supplier:</td>\n <td>\u0627\u0644\u0645\u0648\u0631\u062f:</td>\n </tr>\n\t\t{% if (company.tax_id) %}\n <tr>\n <td>Supplier Tax Identification Number:</td>\n <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0645\u0648\u0631\u062f:</td>\n </tr>\n <tr>\n <td>{{ company.tax_id }}</td>\n <td>{{ company.tax_id }}</td>\n </tr>\n {% endif %}\n <tr>\n <td>{{ company.name }}</td>\n <td>{{ company.company_name_in_arabic }} </td>\n </tr>\n \n \n {% if(supplier_address_doc) %}\n <tr>\n <td>{{ supplier_address_doc.address_line1}} </td>\n <td>{{ supplier_address_doc.address_in_arabic}} </td>\n </tr>\n <tr>\n <td>Phone: {{ supplier_address_doc.phone }}</td>\n <td>\u0647\u0627\u062a\u0641: {{ supplier_address_doc.phone }}</td>\n </tr>\n <tr>\n <td>Email: {{ supplier_address_doc.email_id }}</td>\n <td>\u0628\u0631\u064a\u062f \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a: {{ supplier_address_doc.email_id }}</td>\n </tr>\n {% endif %}\n \n <!-- Customer Info -->\n <tr>\n <td>CUSTOMER:</td>\n <td>\u0639\u0645\u064a\u0644:</td>\n </tr>\n\t\t{% set customer_tax_id = frappe.db.get_value('Customer', doc.customer, 'tax_id') %}\n\t\t{% if customer_tax_id %}\n <tr>\n <td>Customer Tax Identification Number:</td>\n <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0639\u0645\u064a\u0644:</td>\n </tr>\n <tr>\n <td>{{ customer_tax_id }}</td>\n <td>{{ customer_tax_id }}</td>\n </tr>\n {% endif %}\n <tr>\n <td> {{ doc.customer }}</td>\n <td> {{ doc.customer_name_in_arabic }} </td>\n </tr>\n \n {% if(customer_address) %}\n <tr>\n <td>{{ customer_address.address_line1}} </td>\n <td>{{ customer_address.address_in_arabic}} </td>\n </tr>\n {% endif %}\n \n {% if(customer_shipping_address) %}\n <tr>\n <td>SHIPPING ADDRESS:</td>\n <td>\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0634\u062d\u0646:</td>\n </tr>\n \n <tr>\n <td>{{ customer_shipping_address.address_line1}} </td>\n <td>{{ customer_shipping_address.address_in_arabic}} </td>\n </tr>\n {% endif %}\n \n\t\t{% if(doc.po_no) %}\n <tr>\n <td>OTHER INFORMATION</td>\n <td>\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062e\u0631\u0649</td>\n </tr>\n \n <tr>\n <td>Purchase Order Number: {{ doc.po_no }}</td>\n <td>\u0631\u0642\u0645 \u0623\u0645\u0631 \u0627\u0644\u0634\u0631\u0627\u0621: {{ doc.po_no }}</td>\n </tr>\n {% endif %}\n \n <tr>\n <td>Payment Due Date: {{ doc.due_date}} </td>\n <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0627\u0644\u062f\u0641\u0639: {{ doc.due_date}}</td>\n </tr>\n </tbody>\n </table>\n\n <!--Dynamic Colspan for total row columns-->\n {% set col = namespace(one = 2, two = 1) %}\n {% set length = doc.taxes | length %}\n {% set length = length / 2 | round %}\n {% set col.one = col.one + length %}\n {% set col.two = col.two + length %}\n \n {%- if(doc.taxes | length % 2 > 0 ) -%}\n {% set col.two = col.two + 1 %}\n {% endif %}\n \n <!-- Items -->\n {% set total = namespace(amount = 0) %}\n <table class=\"ksa-invoice-table\">\n <thead>\n <tr>\n <th>Nature of goods or services <br />\u0637\u0628\u064a\u0639\u0629 \u0627\u0644\u0633\u0644\u0639 \u0623\u0648 \u0627\u0644\u062e\u062f\u0645\u0627\u062a</th>\n <th>\n Unit price <br />\n \u0633\u0639\u0631 \u0627\u0644\u0648\u062d\u062f\u0629\n </th>\n <th>\n Quantity <br />\n \u0627\u0644\u0643\u0645\u064a\u0629\n </th>\n <th>\n Taxable Amount <br />\n \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u062e\u0627\u0636\u0639 \u0644\u0644\u0636\u0631\u064a\u0628\u0629\n </th>\n \n {% for row in doc.taxes %}\n <th style=\"min-width: 130px\">{{row.description}}</th>\n {% endfor %}\n \n <th>\n Total <br />\n \u0627\u0644\u0645\u062c\u0645\u0648\u0639\n </th>\n </tr>\n </thead>\n <tbody>\n {%- for item in doc.items -%}\n {% set total.amount = item.amount %}\n <tr>\n <td>{{ item.item_code or item.item_name }}</td>\n <td>{{ item.get_formatted(\"rate\") }}</td>\n <td>{{ item.qty }}</td>\n <td>{{ item.get_formatted(\"amount\") }}</td>\n {% for row in doc.taxes %}\n {% set data_object = json.loads(row.item_wise_tax_detail) %}\n {% set key = item.item_code or item.item_name %}\n {% set tax_amount = frappe.utils.flt(data_object[key][1]/doc.conversion_rate, row.precision('tax_amount')) %}\n <td>\n <div class=\"qr-flex\">\n {%- if(data_object[key][0])-%}\n <span>{{ frappe.format(data_object[key][0], {'fieldtype': 'Percent'}) }}</span>\n {%- endif -%}\n <span>\n {%- if(data_object[key][1])-%}\n {{ frappe.format_value(tax_amount, currency=doc.currency) }}</span>\n {% set total.amount = total.amount + tax_amount %}\n {%- endif -%}\n </div>\n </td>\n {% endfor %}\n <td>{{ frappe.format_value(frappe.utils.flt(total.amount, doc.precision('total_taxes_and_charges')), currency=doc.currency) }}</td>\n </tr>\n {%- endfor -%}\n </tbody>\n <tfoot>\n <tr>\n <td>\n {{ doc.get_formatted(\"total\") }} <br />\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n </td>\n \n <td colspan={{ col.one }} class=\"qr-rtl\">\n \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a \u0628\u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n <br />\n \u0625\u062c\u0645\u0627\u0644\u064a \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n </td>\n <td colspan={{ col.two }}>\n Total (Excluding VAT)\n <br />\n Total VAT\n </td>\n <td>\n {{ doc.get_formatted(\"total\") }} <br />\n {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n </td>\n </tr>\n <tr>\n <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n <td colspan={{ col.one }} class=\"qr-rtl\">\n \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u0645\u0633\u062a\u062d\u0642</td>\n <td colspan={{ col.two }}>Total Amount Due</td>\n <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n </tr>\n </tfoot>\n </table>\n\n\t{%- if doc.terms -%}\n <p>\n {{doc.terms}}\n </p>\n\t{%- endif -%}\n</div>\n",
+ "idx": 0,
+ "line_breaks": 0,
+ "margin_bottom": 15.0,
+ "margin_left": 15.0,
+ "margin_right": 15.0,
+ "margin_top": 15.0,
+ "modified": "2021-12-07 13:43:38.018593",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Invoice",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 0,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "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 86aed2e..beac7ed 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -1,4 +1,3 @@
-# coding: utf-8
"""
Provide a report and downloadable CSV according to the German DATEV format.
@@ -7,16 +6,19 @@
- CSV download functionality `download_datev_csv` that provides a CSV file with
all required columns. Used to import the data into the DATEV Software.
"""
-from __future__ import unicode_literals
import json
-import frappe
-from six import string_types
+import frappe
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 +46,12 @@
"width": 100
},
{
+ "label": "BU-Schlüssel",
+ "fieldname": "BU-Schlüssel",
+ "fieldtype": "Data",
+ "width": 100
+ },
+ {
"label": "Belegdatum",
"fieldname": "Belegdatum",
"fieldtype": "Date",
@@ -114,6 +122,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 +199,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 +328,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,9 +343,12 @@
/* 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',
+ REPLACE(LEFT(gl.remarks, 60), '\n', ' ') as 'Buchungstext',
gl.voucher_type as 'Beleginfo - Art 1',
gl.voucher_no as 'Beleginfo - Inhalt 1',
gl.against_voucher_type as 'Beleginfo - Art 2',
@@ -199,30 +358,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
-
- left join `tabParty Account` par
- on par.parent = gl.party
- and par.parenttype = gl.party_type
- and par.company = %(company)s
+ {extra_joins}
WHERE gl.company = %(company)s
AND DATE(gl.posting_date) >= %(from_date)s
AND DATE(gl.posting_date) <= %(to_date)s
- {}
- 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
@@ -380,7 +543,7 @@
Arguments / Params:
filters -- dict of filters to be passed to the sql query
"""
- if isinstance(filters, string_types):
+ if isinstance(filters, str):
filters = json.loads(filters)
validate(filters)
diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py
index 59b878e..14d5495 100644
--- a/erpnext/regional/report/datev/test_datev.py
+++ b/erpnext/regional/report/datev/test_datev.py
@@ -1,22 +1,25 @@
-# coding=utf-8
-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/electronic_invoice_register/electronic_invoice_register.py b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
index 376ba3e..1ae3d16 100644
--- a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
+++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
@@ -1,8 +1,9 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 4f777fc..f3fe5e8 100644
--- a/erpnext/regional/report/eway_bill/eway_bill.py
+++ b/erpnext/regional/report/eway_bill/eway_bill.py
@@ -1,13 +1,15 @@
# Copyright (c) 2013, FinByz Tech Pvt. Ltd. and contributors
# 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 +43,7 @@
}
# Regular expression set to remove all the special characters
- special_characters = "[$%^*()+\\[\]{};':\"\\|<>.?]"
+ special_characters = r"[$%^*()+\\[\]{};':\"\\|<>.?]"
for row in data:
set_defaults(row)
@@ -104,14 +106,14 @@
row.update({'ship_to_state': row.to_state})
def set_taxes(row, filters):
- taxes = frappe.get_list("Sales Taxes and Charges",
+ taxes = frappe.get_all("Sales Taxes and Charges",
filters={
'parent': row.dn_id
},
fields=('item_wise_tax_detail', 'account_head'))
account_list = ["cgst_account", "sgst_account", "igst_account", "cess_account"]
- taxes_list = frappe.get_list("GST Account",
+ taxes_list = frappe.get_all("GST Account",
filters={
"parent": "GST Settings",
"company": filters.company
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..59888ff 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"
@@ -1,12 +1,14 @@
# 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.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 +118,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..528868c 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
@@ -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
-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..386e219 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
@@ -1,10 +1,10 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
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.py b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py
index 12e9676..2d99408 100644
--- a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py
+++ b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py
@@ -1,10 +1,10 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
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),
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..a6f2b3d 100644
--- a/erpnext/regional/report/gst_sales_register/gst_sales_register.py
+++ b/erpnext/regional/report/gst_sales_register/gst_sales_register.py
@@ -1,10 +1,10 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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, 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 9d4f920..11b684d 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -1,15 +1,17 @@
# Copyright (c) 2013, 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 flt, formatdate, now_datetime, getdate
+
+import json
from datetime import date
-from six import iteritems
-from erpnext.regional.doctype.gstr_3b_report.gstr_3b_report import get_period
+
+import frappe
+from frappe import _
+from frappe.utils import flt, formatdate, getdate
+
from erpnext.regional.india.utils import get_gst_accounts
+
def execute(filters=None):
return Gstr1Report(filters).run()
@@ -50,63 +52,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 b2cs_output.items():
+ 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)
@@ -148,12 +170,6 @@
self.invoices = frappe._dict()
conditions = self.get_conditions()
- company_gstins = get_company_gstin_number(self.filters.get('company'), all_gstins=True)
-
- self.filters.update({
- 'company_gstins': company_gstins
- })
-
invoice_data = frappe.db.sql("""
select
{select_columns}
@@ -167,6 +183,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 +205,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 +214,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,10 +224,16 @@
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' """
- conditions += " AND IFNULL(billing_address_gstin, '') NOT IN %(company_gstins)s"
+ conditions += " AND IFNULL(billing_address_gstin, '') != company_gstin"
return conditions
@@ -216,18 +248,17 @@
""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
for d in items:
- if d.item_code not in self.invoice_items.get(d.parent, {}):
- self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
- self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
+ self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
+ self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
- item_tax_rate = {}
+ item_tax_rate = {}
- if d.item_tax_rate:
- item_tax_rate = json.loads(d.item_tax_rate)
+ if d.item_tax_rate:
+ item_tax_rate = json.loads(d.item_tax_rate)
- for account, rate in item_tax_rate.items():
- tax_rate_dict = self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, [])
- tax_rate_dict.append(rate)
+ for account, rate in item_tax_rate.items():
+ tax_rate_dict = self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, [])
+ tax_rate_dict.append(rate)
def get_items_based_on_tax_rate(self):
self.tax_details = frappe.db.sql("""
@@ -284,7 +315,7 @@
+ "<br>" + "<br>".join(unidentified_gst_accounts), alert=True)
# Build itemised tax for export invoices where tax table is blank
- for invoice, items in iteritems(self.invoice_items):
+ for invoice, items in self.invoice_items.items():
if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
and self.invoices.get(invoice, {}).get('export_type') == "Without Payment of Tax" \
and self.invoices.get(invoice, {}).get('gst_category') == "Overseas":
@@ -503,6 +534,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 +687,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()
@@ -616,12 +744,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,
@@ -635,7 +780,7 @@
b2b_item, inv = {"ctin": gst_in, "inv": []}, []
if not gst_in: continue
- for number, invoice in iteritems(res[gst_in]):
+ for number, invoice in res[gst_in].items():
if not invoice[0]["place_of_supply"]:
frappe.throw(_("""{0} not entered in Invoice {1}.
Please update and try again""").format(frappe.bold("Place Of Supply"),
@@ -701,6 +846,40 @@
return out
+def get_advances_json(data, gstin):
+ company_state_number = gstin[0:2]
+ out = []
+ for place_of_supply, items in data.items():
+ 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:
@@ -752,7 +931,7 @@
cdnr_item, inv = {"ctin": gst_in, "nt": []}, []
if not gst_in: continue
- for number, invoice in iteritems(res[gst_in]):
+ for number, invoice in res[gst_in].items():
if not invoice[0]["place_of_supply"]:
frappe.throw(_("""{0} not entered in Invoice {1}.
Please update and try again""").format(frappe.bold("Place Of Supply"),
@@ -780,6 +959,27 @@
return out
+def get_cdnr_unreg_json(res, gstin):
+ out = []
+
+ for invoice, items in res.items():
+ 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 +987,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 +1042,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..47c856d 100644
--- a/erpnext/regional/report/gstr_2/gstr_2.py
+++ b/erpnext/regional/report/gstr_2/gstr_2.py
@@ -1,11 +1,14 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 1adddbd..e03ad37 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
@@ -1,17 +1,19 @@
# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors
# 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
+
+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)
@@ -209,7 +211,7 @@
else:
merged_hsn_dict[row[0]][d['fieldname']] = row[i]
- for key, value in iteritems(merged_hsn_dict):
+ for key, value in merged_hsn_dict.items():
result.append(value)
return result
diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py
index f67d622..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"
diff --git a/erpnext/healthcare/print_format/__init__.py b/erpnext/regional/report/ksa_vat/__init__.py
similarity index 100%
rename from erpnext/healthcare/print_format/__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..59e72c3
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.js
@@ -0,0 +1,59 @@
+// 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();
+ 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..b41b2b0
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.py
@@ -0,0 +1,175 @@
+# Copyright (c) 2013, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+
+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_all(doctype,
+ filters ={
+ 'docstatus': 1,
+ 'posting_date': ['between', [from_date, to_date]]
+ }, fields =['name', 'is_return'])
+
+ for invoice in invoices:
+ invoice_items = frappe.get_all(f'{doctype} Item',
+ filters ={
+ 'docstatus': 1,
+ 'parent': invoice.name,
+ 'item_tax_template': vat_setting.item_tax_template
+ }, fields =['item_code', 'net_amount'])
+
+ 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.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
index 54808e5..def4379 100644
--- a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
+++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
@@ -1,10 +1,14 @@
# 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 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)
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 82423f0..190f408 100644
--- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
+++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
@@ -1,10 +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.utils import getdate
from frappe import _
+from frappe.utils import getdate
+
def execute(filters=None):
data = get_data(filters)
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..4133687 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,20 @@
-# coding=utf-8
-from __future__ import unicode_literals
+from unittest import TestCase
+
+import frappe
import erpnext
-import frappe
-from unittest import TestCase
-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.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.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
index b061423..f8379aa 100644
--- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
@@ -1,10 +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 = get_columns()
data, emirates, amounts_by_emirate = get_data(filters)
@@ -120,7 +121,7 @@
try:
return frappe.db.sql("""
select
- s.vat_emirate as emirate, sum(i.base_amount) as total, sum(s.total_taxes_and_charges)
+ s.vat_emirate as emirate, sum(i.base_amount) as total, sum(i.tax_amount)
from
`tabSales Invoice Item` i inner join `tabSales Invoice` s
on
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..f22abae
--- /dev/null
+++ b/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py
@@ -0,0 +1,194 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+
+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 17aca17..17e5064 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,13 @@
-# 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()
@@ -39,10 +41,11 @@
return self.columns, self.data
def get_sa_vat_accounts(self):
- self.sa_vat_accounts = frappe.get_list("South Africa VAT Account",
+ self.sa_vat_accounts = frappe.get_all("South Africa VAT Account",
filters = {"parent": self.filters.company}, pluck="account")
if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate:
- 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 +72,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 +82,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):
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
index 9b3677d..2e31c03 100644
--- a/erpnext/regional/saudi_arabia/setup.py
+++ b/erpnext/regional/saudi_arabia/setup.py
@@ -1,11 +1,81 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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
+from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
def setup(company=None, patch=True):
- make_custom_fields()
+ uae_custom_fields()
add_print_formats()
+ add_permissions()
+ make_custom_fields()
+
+def add_print_formats():
+ frappe.reload_doc("regional", "print_format", "detailed_tax_invoice", force=True)
+ frappe.reload_doc("regional", "print_format", "simplified_tax_invoice", force=True)
+ frappe.reload_doc("regional", "print_format", "tax_invoice", force=True)
+ frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
+ frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
+
+ for d in ('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice', 'KSA VAT Invoice', 'KSA POS Invoice'):
+ frappe.db.set_value("Print Format", d, "disabled", 0)
+
+def add_permissions():
+ """Add Permissions for KSA VAT Setting."""
+ add_permission('KSA VAT Setting', 'All', 0)
+ for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+ add_permission('KSA VAT Setting', role, 0)
+ update_permission_property('KSA VAT Setting', role, 0, 'write', 1)
+ update_permission_property('KSA VAT Setting', role, 0, 'create', 1)
+
+ """Enable KSA VAT Report"""
+ frappe.db.set_value('Report', 'KSA VAT', 'disabled', 0)
+
+def make_custom_fields():
+ """Create Custom fields
+ - QR code Image file
+ - Company Name in Arabic
+ - Address in Arabic
+ """
+ custom_fields = {
+ 'Sales Invoice': [
+ dict(
+ fieldname='ksa_einv_qr',
+ label='KSA E-Invoicing QR',
+ fieldtype='Attach Image',
+ read_only=1, no_copy=1, hidden=1
+ )
+ ],
+ 'POS Invoice': [
+ dict(
+ fieldname='ksa_einv_qr',
+ label='KSA E-Invoicing QR',
+ fieldtype='Attach Image',
+ read_only=1, no_copy=1, hidden=1
+ )
+ ],
+ 'Address': [
+ dict(
+ fieldname='address_in_arabic',
+ label='Address in Arabic',
+ fieldtype='Data',
+ insert_after='address_line2'
+ )
+ ],
+ 'Company': [
+ dict(
+ fieldname='company_name_in_arabic',
+ label='Company Name In Arabic',
+ fieldtype='Data',
+ insert_after='company_name'
+ )
+ ]
+ }
+
+ create_custom_fields(custom_fields, update=True)
+
+def update_regional_tax_settings(country, company):
+ create_ksa_vat_setting(company)
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
new file mode 100644
index 0000000..a03c3f0
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/utils.py
@@ -0,0 +1,149 @@
+import io
+import os
+from base64 import b64encode
+
+import frappe
+from frappe import _
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+from frappe.utils.data import add_to_date, get_time, getdate
+from pyqrcode import create as qr_create
+
+from erpnext import get_region
+
+
+def create_qr_code(doc, method=None):
+ region = get_region(doc.company)
+ if region not in ['Saudi Arabia']:
+ return
+
+ # if QR Code field not present, create it. Invoices without QR are invalid as per law.
+ if not hasattr(doc, 'ksa_einv_qr'):
+ create_custom_fields({
+ doc.doctype: [
+ dict(
+ fieldname='ksa_einv_qr',
+ label='KSA E-Invoicing QR',
+ fieldtype='Attach Image',
+ read_only=1, no_copy=1, hidden=1
+ )
+ ]
+ })
+
+ # Don't create QR Code if it already exists
+ qr_code = doc.get("ksa_einv_qr")
+ if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}):
+ return
+
+ meta = frappe.get_meta(doc.doctype)
+
+ if "ksa_einv_qr" in [d.fieldname for d in meta.get_image_fields()]:
+ ''' TLV conversion for
+ 1. Seller's Name
+ 2. VAT Number
+ 3. Time Stamp
+ 4. Invoice Amount
+ 5. VAT Amount
+ '''
+ tlv_array = []
+ # Sellers Name
+
+ seller_name = frappe.db.get_value(
+ 'Company',
+ doc.company,
+ 'company_name_in_arabic')
+
+ if not seller_name:
+ frappe.throw(_('Arabic name missing for {} in the company document').format(doc.company))
+
+ tag = bytes([1]).hex()
+ length = bytes([len(seller_name.encode('utf-8'))]).hex()
+ value = seller_name.encode('utf-8').hex()
+ tlv_array.append(''.join([tag, length, value]))
+
+ # VAT Number
+ tax_id = frappe.db.get_value('Company', doc.company, 'tax_id')
+ if not tax_id:
+ frappe.throw(_('Tax ID missing for {} in the company document').format(doc.company))
+
+ tag = bytes([2]).hex()
+ length = bytes([len(tax_id)]).hex()
+ value = tax_id.encode('utf-8').hex()
+ tlv_array.append(''.join([tag, length, value]))
+
+ # Time Stamp
+ posting_date = getdate(doc.posting_date)
+ time = get_time(doc.posting_time)
+ seconds = time.hour * 60 * 60 + time.minute * 60 + time.second
+ time_stamp = add_to_date(posting_date, seconds=seconds)
+ time_stamp = time_stamp.strftime('%Y-%m-%dT%H:%M:%SZ')
+
+ tag = bytes([3]).hex()
+ length = bytes([len(time_stamp)]).hex()
+ value = time_stamp.encode('utf-8').hex()
+ tlv_array.append(''.join([tag, length, value]))
+
+ # Invoice Amount
+ invoice_amount = str(doc.grand_total)
+ tag = bytes([4]).hex()
+ length = bytes([len(invoice_amount)]).hex()
+ value = invoice_amount.encode('utf-8').hex()
+ tlv_array.append(''.join([tag, length, value]))
+
+ # VAT Amount
+ vat_amount = str(doc.total_taxes_and_charges)
+
+ tag = bytes([5]).hex()
+ length = bytes([len(vat_amount)]).hex()
+ value = vat_amount.encode('utf-8').hex()
+ tlv_array.append(''.join([tag, length, value]))
+
+ # Joining bytes into one
+ tlv_buff = ''.join(tlv_array)
+
+ # base64 conversion for QR Code
+ base64_string = b64encode(bytes.fromhex(tlv_buff)).decode()
+
+ qr_image = io.BytesIO()
+ url = qr_create(base64_string, error='L')
+ url.png(qr_image, scale=2, quiet_zone=1)
+
+ name = frappe.generate_hash(doc.name, 5)
+
+ # making file
+ filename = f"QRCode-{name}.png".replace(os.path.sep, "__")
+ _file = frappe.get_doc({
+ "doctype": "File",
+ "file_name": filename,
+ "is_private": 0,
+ "content": qr_image.getvalue(),
+ "attached_to_doctype": doc.get("doctype"),
+ "attached_to_name": doc.get("name"),
+ "attached_to_field": "ksa_einv_qr"
+ })
+
+ _file.save()
+
+ # assigning to document
+ doc.db_set('ksa_einv_qr', _file.file_url)
+ doc.notify_update()
+
+
+def delete_qr_code_file(doc, method=None):
+ region = get_region(doc.company)
+ if region not in ['Saudi Arabia']:
+ return
+
+ if hasattr(doc, 'ksa_einv_qr'):
+ if doc.get('ksa_einv_qr'):
+ file_doc = frappe.get_list('File', {
+ 'file_url': doc.get('ksa_einv_qr')
+ })
+ if len(file_doc):
+ frappe.delete_doc('File', file_doc[0].name)
+
+def delete_vat_settings_for_company(doc, method=None):
+ if doc.country != 'Saudi Arabia':
+ return
+
+ if frappe.db.exists('KSA VAT Setting', doc.name):
+ frappe.delete_doc('KSA VAT Setting', doc.name)
diff --git a/erpnext/healthcare/doctype/inpatient_record/__init__.py b/erpnext/regional/saudi_arabia/wizard/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/inpatient_record/__init__.py
rename to erpnext/regional/saudi_arabia/wizard/__init__.py
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/regional/saudi_arabia/wizard/data/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__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..60951a9
--- /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 Exempted"
+ }
+ ]
+ },
+ {
+ "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 Exempted"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/diagnosis/__init__.py b/erpnext/regional/saudi_arabia/wizard/operations/__init__.py
similarity index 100%
copy from erpnext/healthcare/doctype/diagnosis/__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..97300dc
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
@@ -0,0 +1,43 @@
+import json
+import os
+
+import frappe
+
+
+def create_ksa_vat_setting(company):
+ """On creation of first company. Creates KSA VAT Setting"""
+
+ company = frappe.get_doc('Company', company)
+
+ file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'ksa_vat_settings.json')
+ with open(file_path, 'r') as json_file:
+ account_data = json.load(json_file)
+
+ # 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 8a75987..6af135b 100644
--- a/erpnext/regional/south_africa/setup.py
+++ b/erpnext/regional/south_africa/setup.py
@@ -1,7 +1,6 @@
# 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.custom.doctype.custom_field.custom_field import create_custom_fields
diff --git a/erpnext/regional/turkey/setup.py b/erpnext/regional/turkey/setup.py
index 2396aab..1d3770a 100644
--- a/erpnext/regional/turkey/setup.py
+++ b/erpnext/regional/turkey/setup.py
@@ -1,4 +1,2 @@
-from __future__ import unicode_literals
-
def setup(company=None, patch=True):
pass
diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py
index bd12d66..922443b 100644
--- a/erpnext/regional/united_arab_emirates/setup.py
+++ b/erpnext/regional/united_arab_emirates/setup.py
@@ -1,9 +1,8 @@
# 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, 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..f350ec4 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -1,10 +1,10 @@
-from __future__ import unicode_literals
import frappe
from frappe import _
+from frappe.utils import flt, money_in_words, round_based_on_smallest_currency_fraction
+
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 six import iteritems
+
def update_itemised_tax_data(doc):
if not doc.taxes: return
@@ -21,7 +21,7 @@
# First check if tax rate is present
# If not then look up in item_wise_tax_detail
if item_tax_rate:
- for account, rate in iteritems(item_tax_rate):
+ for account, rate in item_tax_rate.items():
tax_rate += rate
elif row.item_code and itemised_tax.get(row.item_code):
tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py
index 24ab1cf..db6a9c3 100644
--- a/erpnext/regional/united_states/setup.py
+++ b/erpnext/regional/united_states/setup.py
@@ -1,11 +1,18 @@
# 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
+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):
make_custom_fields()
add_print_formats()
diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py
index 513570e..652b483 100644
--- a/erpnext/regional/united_states/test_united_states.py
+++ b/erpnext/regional/united_states/test_united_states.py
@@ -1,8 +1,10 @@
# 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..67838d2 100644
--- a/erpnext/restaurant/doctype/restaurant/restaurant.py
+++ b/erpnext/restaurant/doctype/restaurant/restaurant.py
@@ -1,10 +1,9 @@
-# -*- 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.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 adce5c7..bfdd052 100644
--- a/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py
+++ b/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'restaurant',
diff --git a/erpnext/restaurant/doctype/restaurant/test_restaurant.py b/erpnext/restaurant/doctype/restaurant/test_restaurant.py
index 3ba7f57..f88f980 100644
--- a/erpnext/restaurant/doctype/restaurant/test_restaurant.py
+++ b/erpnext/restaurant/doctype/restaurant/test_restaurant.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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 952c467..64eb40f 100644
--- a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py
+++ b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py
@@ -1,11 +1,11 @@
-# -*- 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.model.document import Document
+
class RestaurantMenu(Document):
def validate(self):
for d in self.items:
diff --git a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py b/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py
index 29f95fd..27020eb 100644
--- a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py
+++ b/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
test_records = [
dict(doctype='Item', item_code='Food Item 1',
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..98b245e 100644
--- a/erpnext/restaurant/doctype/restaurant_menu_item/restaurant_menu_item.py
+++ b/erpnext/restaurant/doctype/restaurant_menu_item/restaurant_menu_item.py
@@ -1,10 +1,9 @@
-# -*- 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.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..f9e75b4 100644
--- a/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py
+++ b/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py
@@ -1,13 +1,16 @@
-# -*- 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, 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..0d9c236 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
@@ -1,10 +1,9 @@
-# -*- 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.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..02ffaf6 100644
--- a/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.py
+++ b/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.py
@@ -1,13 +1,13 @@
-# -*- 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.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..11a3541 100644
--- a/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.py
+++ b/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..29f8a1a 100644
--- a/erpnext/restaurant/doctype/restaurant_table/restaurant_table.py
+++ b/erpnext/restaurant/doctype/restaurant_table/restaurant_table.py
@@ -1,12 +1,13 @@
-# -*- 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, 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..00d14d2 100644
--- a/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.py
+++ b/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
test_records = [
diff --git a/erpnext/selling/doctype/__init__.py b/erpnext/selling/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/__init__.py
+++ b/erpnext/selling/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/customer/__init__.py b/erpnext/selling/doctype/customer/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/customer/__init__.py
+++ b/erpnext/selling/doctype/customer/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 2f06e98..107e4a4 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);
@@ -134,6 +134,12 @@
frm.trigger("get_customer_group_details");
}, __('Actions'));
+ if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
+ frm.add_custom_button(__('Link with Supplier'), function () {
+ frm.trigger('show_party_link_dialog');
+ }, __('Actions'));
+ }
+
// indicator
erpnext.utils.set_party_dashboard_indicators(frm);
@@ -158,5 +164,42 @@
}
});
+ },
+ show_party_link_dialog: function(frm) {
+ const dialog = new frappe.ui.Dialog({
+ title: __('Select a Supplier'),
+ fields: [{
+ fieldtype: 'Link', label: __('Supplier'),
+ options: 'Supplier', fieldname: 'supplier', reqd: 1
+ }],
+ primary_action: function({ supplier }) {
+ frappe.call({
+ method: 'erpnext.accounts.doctype.party_link.party_link.create_party_link',
+ args: {
+ primary_role: 'Customer',
+ primary_party: frm.doc.name,
+ secondary_party: supplier
+ },
+ freeze: true,
+ callback: function() {
+ dialog.hide();
+ frappe.msgprint({
+ message: __('Successfully linked to Supplier'),
+ alert: true
+ });
+ },
+ error: function() {
+ dialog.hide();
+ frappe.msgprint({
+ message: __('Linking to Supplier Failed. Please try again.'),
+ title: __('Linking Failed'),
+ indicator: 'red'
+ });
+ }
+ });
+ },
+ primary_action_label: __('Create Link')
+ });
+ dialog.show();
}
});
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 0d839fc..ae40630 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -16,7 +16,6 @@
"customer_name",
"gender",
"customer_type",
- "pan",
"tax_withholding_category",
"default_bank_account",
"lead_name",
@@ -268,6 +267,7 @@
"options": "fa fa-map-marker"
},
{
+ "depends_on": "eval: !doc.__islocal",
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML",
@@ -284,6 +284,7 @@
"width": "50%"
},
{
+ "depends_on": "eval: !doc.__islocal",
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML",
@@ -485,11 +486,6 @@
"label": "Allow Sales Invoice Creation Without Delivery Note"
},
{
- "fieldname": "pan",
- "fieldtype": "Data",
- "label": "PAN"
- },
- {
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"label": "Tax Withholding Category",
@@ -508,12 +504,19 @@
"idx": 363,
"image_field": "image",
"index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-08-25 18:56:09.929905",
+ "links": [
+ {
+ "group": "Allowed Items",
+ "link_doctype": "Party Specific Item",
+ "link_fieldname": "party"
+ }
+ ],
+ "modified": "2021-10-20 22:07:52.485809",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
"name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index abf146c..0c8c53a 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -1,21 +1,30 @@
# 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.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 ( # noqa
+ get_dashboard_info,
+ get_timeline_data,
+ validate_party_accounts,
+)
+from erpnext.utilities.transaction_base import TransactionBase
+
class Customer(TransactionBase):
def get_feed(self):
@@ -34,8 +43,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 +161,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 +263,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:
@@ -444,11 +467,14 @@
def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, extra_amount=0):
+ credit_limit = get_credit_limit(customer, company)
+ if not credit_limit:
+ return
+
customer_outstanding = get_customer_outstanding(customer, company, ignore_outstanding_sales_order)
if extra_amount > 0:
customer_outstanding += flt(extra_amount)
- credit_limit = get_credit_limit(customer, company)
if credit_limit > 0 and flt(customer_outstanding) > credit_limit:
msgprint(_("Credit limit has been crossed for customer {0} ({1}/{2})")
.format(customer, customer_outstanding, credit_limit))
diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py
index 532c11b..58394d0 100644
--- a/erpnext/selling/doctype/customer/customer_dashboard.py
+++ b/erpnext/selling/doctype/customer/customer_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
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..7d6b74d 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -1,15 +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 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
@@ -17,7 +17,7 @@
test_dependencies = ['Payment Term', 'Payment Terms Template']
test_records = frappe.get_test_records('Customer')
-from six import iteritems
+
class TestCustomer(unittest.TestCase):
def setUp(self):
@@ -89,7 +89,7 @@
details = get_party_details("_Test Customer")
- for key, value in iteritems(to_check):
+ for key, value in to_check.items():
val = details.get(key)
if not val and not isinstance(val, list):
val = None
@@ -253,10 +253,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 +352,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..193027b 100644
--- a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py
+++ b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 CustomerCreditLimit(Document):
pass
diff --git a/erpnext/selling/doctype/industry_type/__init__.py b/erpnext/selling/doctype/industry_type/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/industry_type/__init__.py
+++ b/erpnext/selling/doctype/industry_type/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/industry_type/industry_type.py b/erpnext/selling/doctype/industry_type/industry_type.py
index 7a30d65..fbe0131 100644
--- a/erpnext/selling/doctype/industry_type/industry_type.py
+++ b/erpnext/selling/doctype/industry_type/industry_type.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class IndustryType(Document):
pass
diff --git a/erpnext/selling/doctype/industry_type/test_industry_type.py b/erpnext/selling/doctype/industry_type/test_industry_type.py
index ebc6366..250c2be 100644
--- a/erpnext/selling/doctype/industry_type/test_industry_type.py
+++ b/erpnext/selling/doctype/industry_type/test_industry_type.py
@@ -1,7 +1,6 @@
# 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
+
test_records = frappe.get_test_records('Industry Type')
diff --git a/erpnext/selling/doctype/installation_note/__init__.py b/erpnext/selling/doctype/installation_note/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/installation_note/__init__.py
+++ b/erpnext/selling/doctype/installation_note/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/installation_note/installation_note.py b/erpnext/selling/doctype/installation_note/installation_note.py
index ffcbb2d..36acdbe 100644
--- a/erpnext/selling/doctype/installation_note/installation_note.py
+++ b/erpnext/selling/doctype/installation_note/installation_note.py
@@ -1,16 +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 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..d3c8be5 100644
--- a/erpnext/selling/doctype/installation_note/test_installation_note.py
+++ b/erpnext/selling/doctype/installation_note/test_installation_note.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/selling/doctype/installation_note_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/installation_note_item/__init__.py
+++ b/erpnext/selling/doctype/installation_note_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 7e12052..2169a7b 100644
--- a/erpnext/selling/doctype/installation_note_item/installation_note_item.py
+++ b/erpnext/selling/doctype/installation_note_item/installation_note_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class InstallationNoteItem(Document):
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..2bb876e 100644
--- a/erpnext/selling/doctype/product_bundle/product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/product_bundle.py
@@ -1,14 +1,12 @@
# 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 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.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
index 7d1d372..c1e2fde 100644
--- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
@@ -1,10 +1,8 @@
-
# 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
+
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..5c95a55 100644
--- a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py
+++ b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProductBundleItem(Document):
pass
diff --git a/erpnext/selling/doctype/quotation/__init__.py b/erpnext/selling/doctype/quotation/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/quotation/__init__.py
+++ b/erpnext/selling/doctype/quotation/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..ee5b0ea 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",
@@ -108,7 +110,8 @@
"enq_det",
"supplier_quotation",
"opportunity",
- "lost_reasons"
+ "lost_reasons",
+ "competitors"
],
"fields": [
{
@@ -926,17 +929,43 @@
"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
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "competitors",
+ "fieldtype": "Table MultiSelect",
+ "label": "Competitors",
+ "options": "Competitor Detail",
+ "read_only": 1
}
],
"icon": "fa fa-shopping-cart",
"idx": 82,
"is_submittable": 1,
"links": [],
- "max_attachments": 1,
- "modified": "2020-10-30 13:58:59.212060",
+ "modified": "2021-11-30 01:33:21.106073",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index e4f8a47..c4752ae 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -1,11 +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
-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 +31,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"))
@@ -65,7 +68,7 @@
opp.set_status(status=status, update=True)
@frappe.whitelist()
- def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
+ def declare_enquiry_lost(self, lost_reasons_list, competitors, detailed_reason=None):
if not self.has_sales_order():
get_lost_reasons = frappe.get_list('Quotation Lost Reason',
fields = ["name"])
@@ -81,6 +84,9 @@
else:
frappe.throw(_("Invalid lost reason {0}, please create a new lost reason").format(frappe.bold(reason.get('lost_reason'))))
+ for competitor in competitors:
+ self.append('competitors', competitor)
+
self.update_opportunity('Lost')
self.update_lead()
self.save()
diff --git a/erpnext/selling/doctype/quotation/quotation_dashboard.py b/erpnext/selling/doctype/quotation/quotation_dashboard.py
index d1bb788..0a1aad7 100644
--- a/erpnext/selling/doctype/quotation/quotation_dashboard.py
+++ b/erpnext/selling/doctype/quotation/quotation_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'prevdoc_docname',
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 527a999..aa83726 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -1,10 +1,10 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-from frappe.utils import flt, add_days, nowdate, add_months, getdate
-import unittest
+from frappe.utils import add_days, add_months, flt, getdate, nowdate
test_dependencies = ["Product Bundle"]
@@ -133,8 +133,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 +228,190 @@
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)
+
+ def test_product_bundle_price_calculation_for_multiple_product_bundles_when_calculate_bundle_price_is_checked(self):
+ from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ make_item("_Test Product Bundle 1", {"is_stock_item": 0})
+ make_item("_Test Product Bundle 2", {"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_item("_Test Bundle Item 3", {"is_stock_item": 1})
+
+ make_product_bundle("_Test Product Bundle 1",
+ ["_Test Bundle Item 1", "_Test Bundle Item 2"])
+ make_product_bundle("_Test Product Bundle 2",
+ ["_Test Bundle Item 2", "_Test Bundle Item 3"])
+
+ enable_calculate_bundle_price()
+
+ item_list = [
+ {
+ "item_code": "_Test Product Bundle 1",
+ "warehouse": "",
+ "qty": 1,
+ "rate": 400,
+ "delivered_by_supplier": 1,
+ "supplier": '_Test Supplier'
+ },
+ {
+ "item_code": "_Test Product Bundle 2",
+ "warehouse": "",
+ "qty": 1,
+ "rate": 400,
+ "delivered_by_supplier": 1,
+ "supplier": '_Test Supplier'
+ }
+ ]
+
+ quotation = make_quotation(item_list=item_list, do_not_submit=1)
+ quotation.packed_items[0].rate = 100
+ quotation.packed_items[1].rate = 200
+ quotation.packed_items[2].rate = 200
+ quotation.packed_items[3].rate = 300
+ quotation.save()
+
+ expected_values = [300, 500]
+
+ for item in quotation.items:
+ self.assertEqual(item.amount, expected_values[item.idx-1])
+
+ enable_calculate_bundle_price(enable=0)
+
+ def test_packed_items_indices_are_reset_when_product_bundle_is_deleted_from_items_table(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 1", {"is_stock_item": 0})
+ make_item("_Test Product Bundle 2", {"is_stock_item": 0})
+ make_item("_Test Product Bundle 3", {"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_item("_Test Bundle Item 3", {"is_stock_item": 1})
+
+ make_product_bundle("_Test Product Bundle 1",
+ ["_Test Bundle Item 1", "_Test Bundle Item 2"])
+ make_product_bundle("_Test Product Bundle 2",
+ ["_Test Bundle Item 2", "_Test Bundle Item 3"])
+ make_product_bundle("_Test Product Bundle 3",
+ ["_Test Bundle Item 3", "_Test Bundle Item 1"])
+
+ item_list = [
+ {
+ "item_code": "_Test Product Bundle 1",
+ "warehouse": "",
+ "qty": 1,
+ "rate": 400,
+ "delivered_by_supplier": 1,
+ "supplier": '_Test Supplier'
+ },
+ {
+ "item_code": "_Test Product Bundle 2",
+ "warehouse": "",
+ "qty": 1,
+ "rate": 400,
+ "delivered_by_supplier": 1,
+ "supplier": '_Test Supplier'
+ },
+ {
+ "item_code": "_Test Product Bundle 3",
+ "warehouse": "",
+ "qty": 1,
+ "rate": 400,
+ "delivered_by_supplier": 1,
+ "supplier": '_Test Supplier'
+ }
+ ]
+
+ quotation = make_quotation(item_list=item_list, do_not_submit=1)
+ del quotation.items[1]
+ quotation.save()
+
+ for id, item in enumerate(quotation.packed_items):
+ expected_index = id + 1
+ self.assertEqual(item.idx, expected_index)
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_item/__init__.py b/erpnext/selling/doctype/quotation_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/quotation_item/__init__.py
+++ b/erpnext/selling/doctype/quotation_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.py b/erpnext/selling/doctype/quotation_item/quotation_item.py
index 7384871..8c2aabb 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.py
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class QuotationItem(Document):
pass
diff --git a/erpnext/selling/doctype/sales_order/__init__.py b/erpnext/selling/doctype/sales_order/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/sales_order/__init__.py
+++ b/erpnext/selling/doctype/sales_order/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index b42c615..79e9e17 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'
@@ -75,6 +78,8 @@
});
erpnext.queries.setup_warehouse_query(frm);
+
+ frm.ignore_doctypes_on_cancel_all = ['Purchase Order'];
},
delivery_date: function(frm) {
@@ -314,7 +319,7 @@
title: __('Select Items to Manufacture'),
fields: fields,
primary_action: function() {
- var data = d.get_values();
+ var data = {items: d.fields_dict.items.grid.get_selected_children()};
me.frm.call({
method: 'make_work_orders',
args: {
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 38ea5c8..7e99a06 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",
@@ -134,6 +134,7 @@
"sales_team_section_break",
"sales_partner",
"column_break7",
+ "amount_eligible_for_commission",
"commission_rate",
"total_commission",
"section_break1",
@@ -1019,6 +1020,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "packed_items",
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"hide_days": 1,
@@ -1029,14 +1031,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",
@@ -1479,6 +1481,7 @@
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
"read_only": 1
@@ -1505,16 +1508,23 @@
"fieldtype": "Small Text",
"label": "Dispatch Address",
"read_only": 1
+ },
+ {
+ "fieldname": "amount_eligible_for_commission",
+ "fieldtype": "Currency",
+ "label": "Amount Eligible for Commission",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2021-08-17 20:15:26.531553",
+ "modified": "2021-10-05 12:16:40.775704",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index bba5401..cc95185 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1,25 +1,31 @@
# 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
+
+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 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"
@@ -57,6 +63,8 @@
if not self.billing_status: self.billing_status = 'Not Billed'
if not self.delivery_status: self.delivery_status = 'Not Delivered'
+ self.reset_default_field_value("set_warehouse", "items", "warehouse")
+
def validate_po(self):
# validate p.o date v/s delivery date
if self.po_date and not self.skip_delivery_note:
@@ -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"))
@@ -219,60 +225,15 @@
check_credit_limit(self.customer, self.company)
def check_nextdoc_docstatus(self):
- # Checks Delivery Note
- submit_dn = frappe.db.sql_list("""
- select t1.name
- from `tabDelivery Note` t1,`tabDelivery Note Item` t2
- where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1""", self.name)
-
- if submit_dn:
- submit_dn = [get_link_to_form("Delivery Note", dn) for dn in submit_dn]
- frappe.throw(_("Delivery Notes {0} must be cancelled before cancelling this Sales Order")
- .format(", ".join(submit_dn)))
-
- # Checks Sales Invoice
- submit_rv = frappe.db.sql_list("""select t1.name
+ linked_invoices = frappe.db.sql_list("""select distinct t1.name
from `tabSales Invoice` t1,`tabSales Invoice Item` t2
- where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus < 2""",
+ where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 0""",
self.name)
- if submit_rv:
- submit_rv = [get_link_to_form("Sales Invoice", si) for si in submit_rv]
- frappe.throw(_("Sales Invoice {0} must be cancelled before cancelling this Sales Order")
- .format(", ".join(submit_rv)))
-
- #check maintenance schedule
- submit_ms = frappe.db.sql_list("""
- select t1.name
- from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2
- where t2.parent=t1.name and t2.sales_order = %s and t1.docstatus = 1""", self.name)
-
- if submit_ms:
- submit_ms = [get_link_to_form("Maintenance Schedule", ms) for ms in submit_ms]
- frappe.throw(_("Maintenance Schedule {0} must be cancelled before cancelling this Sales Order")
- .format(", ".join(submit_ms)))
-
- # check maintenance visit
- submit_mv = frappe.db.sql_list("""
- select t1.name
- from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2
- where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1""",self.name)
-
- if submit_mv:
- submit_mv = [get_link_to_form("Maintenance Visit", mv) for mv in submit_mv]
- frappe.throw(_("Maintenance Visit {0} must be cancelled before cancelling this Sales Order")
- .format(", ".join(submit_mv)))
-
- # check work order
- pro_order = frappe.db.sql_list("""
- select name
- from `tabWork Order`
- where sales_order = %s and docstatus = 1""", self.name)
-
- if pro_order:
- pro_order = [get_link_to_form("Work Order", po) for po in pro_order]
- frappe.throw(_("Work Order {0} must be cancelled before cancelling this Sales Order")
- .format(", ".join(pro_order)))
+ if linked_invoices:
+ linked_invoices = [get_link_to_form("Sales Invoice", si) for si in linked_invoices]
+ frappe.throw(_("Sales Invoice {0} must be deleted before cancelling this Sales Order")
+ .format(", ".join(linked_invoices)))
def check_modified_date(self):
mod_db = frappe.db.get_value("Sales Order", self.name, "modified")
@@ -718,8 +679,7 @@
"doctype": "Maintenance Schedule Item",
"field_map": {
"parent": "sales_order"
- },
- "add_if_empty": True
+ }
}
}, target_doc)
@@ -745,8 +705,7 @@
"field_map": {
"parent": "prevdoc_docname",
"parenttype": "prevdoc_doctype"
- },
- "add_if_empty": True
+ }
}
}, target_doc)
@@ -787,7 +746,7 @@
"""Creates Purchase Order for each Supplier. Returns a list of doc objects."""
if not selected_items: return
- if isinstance(selected_items, string_types):
+ if isinstance(selected_items, str):
selected_items = json.loads(selected_items)
def set_missing_values(source, target):
@@ -888,7 +847,7 @@
def make_purchase_order(source_name, selected_items=None, target_doc=None):
if not selected_items: return
- if isinstance(selected_items, string_types):
+ if isinstance(selected_items, str):
selected_items = json.loads(selected_items)
items_to_map = [item.get('item_code') for item in selected_items if item.get('item_code') and item.get('item_code')]
@@ -949,11 +908,53 @@
"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"
+ ],
+ "condition": lambda doc: doc.parent_item in items_to_map
}
}, 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`'''
@@ -979,6 +980,7 @@
description=i['description']
)).insert()
work_order.set_work_order_operations()
+ work_order.flags.ignore_mandatory = True
work_order.save()
out.append(work_order)
@@ -1001,7 +1003,7 @@
if not frappe.has_permission("Sales Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
- if isinstance(items, string_types):
+ if isinstance(items, str):
items = frappe._dict(json.loads(items))
for item in items.get('items'):
diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
index 2a71c27..1e616b8 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
+++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'sales_order',
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 d685fbf..2a0752e 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1,21 +1,34 @@
# 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.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
+ make_maintenance_schedule,
+)
+from erpnext.maintenance.doctype.maintenance_visit.test_maintenance_visit import (
+ make_maintenance_visit,
+)
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 +175,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 +457,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 +744,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 +830,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 +886,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 +925,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 +1094,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 +1102,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 +1136,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 +1175,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()
@@ -1231,8 +1284,76 @@
self.assertRaises(frappe.ValidationError, so.cancel)
+ def test_so_cancellation_after_si_submission(self):
+ """
+ Test to check if Sales Order gets cancelled when linked Sales Invoice has been Submitted
+ Expected result: Sales Order should not get cancelled
+ """
+ so = make_sales_order()
+ so.submit()
+ si = make_sales_invoice(so.name)
+ si.submit()
+
+ so.load_from_db()
+ self.assertRaises(frappe.LinkExistsError, so.cancel)
+
+ def test_so_cancellation_after_dn_submission(self):
+ """
+ Test to check if Sales Order gets cancelled when linked Delivery Note has been Submitted
+ Expected result: Sales Order should not get cancelled
+ """
+ so = make_sales_order()
+ so.submit()
+ dn = make_delivery_note(so.name)
+ dn.submit()
+
+ so.load_from_db()
+ self.assertRaises(frappe.LinkExistsError, so.cancel)
+
+ def test_so_cancellation_after_maintenance_schedule_submission(self):
+ """
+ Expected result: Sales Order should not get cancelled
+ """
+ so = make_sales_order()
+ so.submit()
+ ms = make_maintenance_schedule()
+ ms.items[0].sales_order = so.name
+ ms.submit()
+
+ so.load_from_db()
+ self.assertRaises(frappe.LinkExistsError, so.cancel)
+
+ def test_so_cancellation_after_maintenance_visit_submission(self):
+ """
+ Expected result: Sales Order should not get cancelled
+ """
+ so = make_sales_order()
+ so.submit()
+ mv = make_maintenance_visit()
+ mv.purposes[0].prevdoc_doctype = "Sales Order"
+ mv.purposes[0].prevdoc_docname = so.name
+ mv.submit()
+
+ so.load_from_db()
+ self.assertRaises(frappe.LinkExistsError, so.cancel)
+
+ def test_so_cancellation_after_work_order_submission(self):
+ """
+ Expected result: Sales Order should not get cancelled
+ """
+ from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
+
+ so = make_sales_order(item_code="_Test FG Item", qty=10)
+ so.submit()
+ make_wo_order_test_record(sales_order=so.name)
+
+ so.load_from_db()
+ self.assertRaises(frappe.LinkExistsError, 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()
@@ -1332,7 +1453,6 @@
frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True)
frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True)
- frappe.db.commit()
frappe.cache().hdel('roles', frappe.session.user)
workflow = frappe.get_doc({
diff --git a/erpnext/selling/doctype/sales_order_item/__init__.py b/erpnext/selling/doctype/sales_order_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/sales_order_item/__init__.py
+++ b/erpnext/selling/doctype/sales_order_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 1e5590e..95f6c4e 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -48,6 +48,7 @@
"pricing_rules",
"stock_uom_rate",
"is_free_item",
+ "grant_commission",
"section_break_24",
"net_rate",
"net_amount",
@@ -789,15 +790,23 @@
"no_copy": 1,
"options": "currency",
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "grant_commission",
+ "fieldtype": "Check",
+ "label": "Grant Commission",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-02-23 01:15:05.803091",
+ "modified": "2021-10-05 12:27:25.014789",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
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 62afef3..441a6ac 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.py
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.py
@@ -1,11 +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 frappe
from frappe.model.document import Document
+
class SalesOrderItem(Document):
pass
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..0a07073 100644
--- a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.py
+++ b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.py
@@ -1,9 +1,9 @@
-# -*- 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 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..04d4089 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import unittest
+
class TestSalesPartnerType(unittest.TestCase):
pass
diff --git a/erpnext/selling/doctype/sales_team/__init__.py b/erpnext/selling/doctype/sales_team/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/sales_team/__init__.py
+++ b/erpnext/selling/doctype/sales_team/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/sales_team/sales_team.json b/erpnext/selling/doctype/sales_team/sales_team.json
index 8767891..cac5b76 100644
--- a/erpnext/selling/doctype/sales_team/sales_team.json
+++ b/erpnext/selling/doctype/sales_team/sales_team.json
@@ -1,247 +1,100 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-04-19 13:30:51",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "creation": "2013-04-19 13:30:51",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "sales_person",
+ "contact_no",
+ "allocated_percentage",
+ "allocated_amount",
+ "commission_rate",
+ "incentives"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sales_person",
- "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": "Sales Person",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "sales_person",
- "oldfieldtype": "Link",
- "options": "Sales Person",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "200px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 1,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0,
+ "allow_on_submit": 1,
+ "fieldname": "sales_person",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Sales Person",
+ "oldfieldname": "sales_person",
+ "oldfieldtype": "Link",
+ "options": "Sales Person",
+ "print_width": "200px",
+ "reqd": 1,
+ "search_index": 1,
"width": "200px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "contact_no",
- "fieldtype": "Data",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Contact No.",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "contact_no",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "100px",
- "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_on_submit": 1,
+ "fieldname": "contact_no",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "in_list_view": 1,
+ "label": "Contact No.",
+ "oldfieldname": "contact_no",
+ "oldfieldtype": "Data",
+ "print_width": "100px",
"width": "100px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "allocated_percentage",
- "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": "Contribution (%)",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "allocated_percentage",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "100px",
- "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_on_submit": 1,
+ "fieldname": "allocated_percentage",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Contribution (%)",
+ "oldfieldname": "allocated_percentage",
+ "oldfieldtype": "Currency",
+ "print_width": "100px",
"width": "100px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "allocated_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Contribution to Net Total",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "allocated_amount",
- "oldfieldtype": "Currency",
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "120px",
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0,
+ "allow_on_submit": 1,
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Contribution to Net Total",
+ "oldfieldname": "allocated_amount",
+ "oldfieldtype": "Currency",
+ "options": "Company:company:default_currency",
+ "print_width": "120px",
+ "read_only": 1,
"width": "120px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "commission_rate",
- "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": "Commission Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "sales_person.commission_rate",
+ "fetch_if_empty": 1,
+ "fieldname": "commission_rate",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Commission Rate",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "incentives",
- "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": "Incentives",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "incentives",
- "oldfieldtype": "Currency",
- "options": "Company:company:default_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
+ "allow_on_submit": 1,
+ "fieldname": "incentives",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Incentives",
+ "oldfieldname": "incentives",
+ "oldfieldtype": "Currency",
+ "options": "Company:company:default_currency"
}
- ],
- "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-09-17 13:03:14.755974",
- "modified_by": "Administrator",
- "module": "Selling",
- "name": "Sales Team",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "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-11-09 23:55:20.670475",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Team",
+ "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/selling/doctype/sales_team/sales_team.py b/erpnext/selling/doctype/sales_team/sales_team.py
index 28bea25..d3eae3a 100644
--- a/erpnext/selling/doctype/sales_team/sales_team.py
+++ b/erpnext/selling/doctype/sales_team/sales_team.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class SalesTeam(Document):
pass
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js
index d8d3051..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.")
- }
-];
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index 717fd9b..27bc541 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -11,24 +11,20 @@
"customer_group",
"column_break_4",
"territory",
- "crm_settings_section",
- "campaign_naming_by",
- "default_valid_till",
- "column_break_9",
- "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 +36,7 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "Customer Naming By",
- "options": "Customer Name\nNaming Series"
- },
- {
- "fieldname": "campaign_naming_by",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Campaign Naming By",
- "options": "Campaign Name\nNaming Series"
+ "options": "Customer Name\nNaming Series\nAuto Name"
},
{
"fieldname": "customer_group",
@@ -71,18 +60,6 @@
"options": "Price List"
},
{
- "default": "15",
- "description": "Auto close Opportunity after the no. of days mentioned above",
- "fieldname": "close_opportunity_after_days",
- "fieldtype": "Int",
- "label": "Close Opportunity After Days"
- },
- {
- "fieldname": "default_valid_till",
- "fieldtype": "Data",
- "label": "Default Quotation Validity Days"
- },
- {
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
@@ -146,15 +123,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",
@@ -170,15 +146,6 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "crm_settings_section",
- "fieldtype": "Section Break",
- "label": "CRM Settings"
- },
- {
- "fieldname": "column_break_9",
- "fieldtype": "Column Break"
- },
- {
"fieldname": "item_price_settings_section",
"fieldtype": "Section Break",
"label": "Item Price Settings"
@@ -191,6 +158,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 +171,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-08-06 22:25:50.119458",
+ "modified": "2021-09-13 12:32:17.004404",
"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..fb86e61 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.py
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.py
@@ -3,18 +3,17 @@
# 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.utils.nestedset import get_root_of
+import frappe
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.model.document import Document
+from frappe.utils import cint
+
class 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,8 +32,7 @@
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 set_default_customer_group_and_territory(self):
- if not self.customer_group:
- self.customer_group = get_root_of('Customer Group')
- if not self.territory:
- self.territory = get_root_of('Territory')
+ 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)
diff --git a/erpnext/selling/doctype/selling_settings/test_selling_settings.py b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
index 961a54d..fc6754a 100644
--- a/erpnext/selling/doctype/selling_settings/test_selling_settings.py
+++ b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestSellingSettings(unittest.TestCase):
pass
diff --git a/erpnext/selling/doctype/sms_center/__init__.py b/erpnext/selling/doctype/sms_center/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/doctype/sms_center/__init__.py
+++ b/erpnext/selling/doctype/sms_center/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py
index 87846a8..d192457 100644
--- a/erpnext/selling/doctype/sms_center/sms_center.py
+++ b/erpnext/selling/doctype/sms_center/sms_center.py
@@ -1,15 +1,13 @@
# 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 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()
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/__init__.py b/erpnext/selling/page/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/selling/page/__init__.py
+++ b/erpnext/selling/page/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 03c46bb..db5b20e 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.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, 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 {}
@@ -209,7 +212,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_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 9d8338e..4920584 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -49,11 +49,11 @@
this.$component.append(
`<div class="cart-container">
<div class="abs-cart-container">
- <div class="cart-label">Item Cart</div>
+ <div class="cart-label">${__('Item Cart')}</div>
<div class="cart-header">
- <div class="name-header">Item</div>
- <div class="qty-header">Qty</div>
- <div class="rate-amount-header">Amount</div>
+ <div class="name-header">${__('Item')}</div>
+ <div class="qty-header">${__('Quantity')}</div>
+ <div class="rate-amount-header">${__('Amount')}</div>
</div>
<div class="cart-items-section"></div>
<div class="cart-totals-section"></div>
@@ -78,7 +78,7 @@
make_no_items_placeholder() {
this.$cart_header.css('display', 'none');
this.$cart_items_wrapper.html(
- `<div class="no-item-wrapper">No items in cart</div>`
+ `<div class="no-item-wrapper">${__('No items in cart')}</div>`
);
}
@@ -98,19 +98,23 @@
this.$totals_section.append(
`<div class="add-discount-wrapper">
- ${this.get_discount_icon()} Add Discount
+ ${this.get_discount_icon()} ${__('Add Discount')}
+ </div>
+ <div class="item-qty-total-container">
+ <div class="item-qty-total-label">${__('Total Items')}</div>
+ <div class="item-qty-total-value">0.00</div>
</div>
<div class="net-total-container">
- <div class="net-total-label">Net Total</div>
+ <div class="net-total-label">${__("Net Total")}</div>
<div class="net-total-value">0.00</div>
</div>
<div class="taxes-container"></div>
<div class="grand-total-container">
- <div>Grand Total</div>
+ <div>${__('Grand Total')}</div>
<div>0.00</div>
</div>
- <div class="checkout-btn">Checkout</div>
- <div class="edit-cart-btn">Edit Cart</div>`
+ <div class="checkout-btn">${__('Checkout')}</div>
+ <div class="edit-cart-btn">${__('Edit Cart')}</div>`
)
this.$add_discount_elem = this.$component.find(".add-discount-wrapper");
@@ -126,10 +130,10 @@
},
cols: 5,
keys: [
- [ 1, 2, 3, 'Quantity' ],
- [ 4, 5, 6, 'Discount' ],
- [ 7, 8, 9, 'Rate' ],
- [ '.', 0, 'Delete', 'Remove' ]
+ [ 1, 2, 3, __('Quantity') ],
+ [ 4, 5, 6, __('Discount') ],
+ [ 7, 8, 9, __('Rate') ],
+ [ '.', 0, __('Delete'), __('Remove') ]
],
css_classes: [
[ '', '', '', 'col-span-2' ],
@@ -142,13 +146,14 @@
this.$numpad_section.prepend(
`<div class="numpad-totals">
+ <span class="numpad-item-qty-total"></span>
<span class="numpad-net-total"></span>
<span class="numpad-grand-total"></span>
</div>`
)
this.$numpad_section.append(
- `<div class="numpad-btn checkout-btn" data-button-value="checkout">Checkout</div>`
+ `<div class="numpad-btn checkout-btn" data-button-value="checkout">${__('Checkout')}</div>`
)
}
@@ -386,7 +391,7 @@
'border': '1px dashed var(--gray-500)',
'padding': 'var(--padding-sm) var(--padding-md)'
});
- me.$add_discount_elem.html(`${me.get_discount_icon()} Add Discount`);
+ me.$add_discount_elem.html(`${me.get_discount_icon()} ${__('Add Discount')}`);
me.discount_field = undefined;
}
},
@@ -411,7 +416,7 @@
});
this.$add_discount_elem.html(
`<div class="edit-discount-btn">
- ${this.get_discount_icon()} Additional ${String(discount).bold()}% discount applied
+ ${this.get_discount_icon()} ${__("Additional")} ${String(discount).bold()}% ${__("discount applied")}
</div>`
);
}
@@ -445,7 +450,7 @@
function get_customer_description() {
if (!email_id && !mobile_no) {
- return `<div class="customer-desc">Click to add email / phone</div>`;
+ return `<div class="customer-desc">${__('Click to add email / phone')}</div>`;
} else if (email_id && !mobile_no) {
return `<div class="customer-desc">${email_id}</div>`;
} else if (mobile_no && !email_id) {
@@ -470,6 +475,7 @@
if (!frm) frm = this.events.get_frm();
this.render_net_total(frm.doc.net_total);
+ this.render_total_item_qty(frm.doc.items);
const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? frm.doc.grand_total : frm.doc.rounded_total;
this.render_grand_total(grand_total);
@@ -479,22 +485,37 @@
render_net_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.net-total-container').html(
- `<div>Net Total</div><div>${format_currency(value, currency)}</div>`
+ `<div>${__('Net Total')}</div><div>${format_currency(value, currency)}</div>`
)
this.$numpad_section.find('.numpad-net-total').html(
- `<div>Net Total: <span>${format_currency(value, currency)}</span></div>`
+ `<div>${__('Net Total')}: <span>${format_currency(value, currency)}</span></div>`
+ );
+ }
+
+ render_total_item_qty(items) {
+ var total_item_qty = 0;
+ items.map((item) => {
+ total_item_qty = total_item_qty + item.qty;
+ });
+
+ this.$totals_section.find('.item-qty-total-container').html(
+ `<div>${__('Total Quantity')}</div><div>${total_item_qty}</div>`
+ );
+
+ this.$numpad_section.find('.numpad-item-qty-total').html(
+ `<div>${__('Total Quantity')}: <span>${total_item_qty}</span></div>`
);
}
render_grand_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.grand-total-container').html(
- `<div>Grand Total</div><div>${format_currency(value, currency)}</div>`
+ `<div>${__('Grand Total')}</div><div>${format_currency(value, currency)}</div>`
)
this.$numpad_section.find('.numpad-grand-total').html(
- `<div>Grand Total: <span>${format_currency(value, currency)}</span></div>`
+ `<div>${__('Grand Total')}: <span>${format_currency(value, currency)}</span></div>`
);
}
@@ -502,6 +523,7 @@
if (taxes.length) {
const currency = this.events.get_frm().doc.currency;
const taxes_html = taxes.map(t => {
+ if (t.tax_amount_after_discount_amount == 0.0) return;
const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
return `<div class="tax-row">
<div class="tax-label">${description}</div>
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 d899c5c..fb69b63 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -28,7 +28,7 @@
init_child_components() {
this.$component.html(
`<div class="item-details-header">
- <div class="label">Item Details</div>
+ <div class="label">${__('Item Details')}</div>
<div class="close-btn">
<svg width="32" height="32" viewBox="0 0 14 14" fill="none">
<path d="M4.93764 4.93759L7.00003 6.99998M9.06243 9.06238L7.00003 6.99998M7.00003 6.99998L4.93764 9.06238L9.06243 4.93759" stroke="#8D99A6"/>
@@ -201,8 +201,9 @@
`<div class="grid-filler no-select"></div>`
);
}
+ const label = __('Auto Fetch Serial Numbers');
this.$form_container.append(
- `<div class="btn btn-sm btn-secondary auto-fetch-btn">Auto Fetch Serial Numbers</div>`
+ `<div class="btn btn-sm btn-secondary auto-fetch-btn">${label}</div>`
);
this.$form_container.find('.serial_no-control').find('textarea').css('height', '6rem');
}
@@ -236,7 +237,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
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 8352b14..4963852 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -24,7 +24,7 @@
this.wrapper.append(
`<section class="items-selector">
<div class="filter-section">
- <div class="label">All Items</div>
+ <div class="label">${__('All Items')}</div>
<div class="search-field"></div>
<div class="item-group-field"></div>
</div>
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 e0993e2..a0475c7 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
@@ -16,7 +16,7 @@
this.wrapper.append(
`<section class="past-order-list">
<div class="filter-section">
- <div class="label">Recent Orders</div>
+ <div class="label">${__('Recent Orders')}</div>
<div class="search-field"></div>
<div class="status-field"></div>
</div>
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 dd9e05a..eeb8523 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
@@ -17,16 +17,16 @@
this.wrapper.append(
`<section class="past-order-summary">
<div class="no-summary-placeholder">
- Select an invoice to load summary data
+ ${__('Select an invoice to load summary data')}
</div>
<div class="invoice-summary-wrapper">
<div class="abs-container">
<div class="upper-section"></div>
- <div class="label">Items</div>
+ <div class="label">${__('Items')}</div>
<div class="items-container summary-container"></div>
- <div class="label">Totals</div>
+ <div class="label">${__('Totals')}</div>
<div class="totals-container summary-container"></div>
- <div class="label">Payments</div>
+ <div class="label">${__('Payments')}</div>
<div class="payments-container summary-container"></div>
<div class="summary-btns"></div>
</div>
@@ -82,7 +82,7 @@
return `<div class="left-section">
<div class="customer-name">${doc.customer}</div>
<div class="customer-email">${this.customer_email}</div>
- <div class="cashier">Sold by: ${doc.owner}</div>
+ <div class="cashier">${__('Sold by')}: ${doc.owner}</div>
</div>
<div class="right-section">
<div class="paid-amount">${format_currency(doc.paid_amount, doc.currency)}</div>
@@ -121,7 +121,7 @@
get_net_total_html(doc) {
return `<div class="summary-row-wrapper">
- <div>Net Total</div>
+ <div>${__('Net Total')}</div>
<div>${format_currency(doc.net_total, doc.currency)}</div>
</div>`;
}
@@ -144,14 +144,14 @@
get_grand_total_html(doc) {
return `<div class="summary-row-wrapper grand-total">
- <div>Grand Total</div>
+ <div>${__('Grand Total')}</div>
<div>${format_currency(doc.grand_total, doc.currency)}</div>
</div>`;
}
get_payment_html(doc, payment) {
return `<div class="summary-row-wrapper payments">
- <div>${payment.mode_of_payment}</div>
+ <div>${__(payment.mode_of_payment)}</div>
<div>${format_currency(payment.amount, doc.currency)}</div>
</div>`;
}
@@ -285,8 +285,9 @@
if (m.condition) {
m.visible_btns.forEach(b => {
const class_name = b.split(' ')[0].toLowerCase();
+ const btn = __(b);
this.$summary_btns.append(
- `<div class="summary-btn btn btn-default ${class_name}-btn">${b}</div>`
+ `<div class="summary-btn btn btn-default ${class_name}-btn">${btn}</div>`
);
});
}
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 63306ad..b9b6559 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -18,11 +18,11 @@
prepare_dom() {
this.wrapper.append(
`<section class="payment-container">
- <div class="section-label payment-section">Payment Method</div>
+ <div class="section-label payment-section">${__('Payment Method')}</div>
<div class="payment-modes"></div>
<div class="fields-numpad-container">
<div class="fields-section">
- <div class="section-label">Additional Information</div>
+ <div class="section-label">${__('Additional Information')}</div>
<div class="invoice-fields"></div>
</div>
<div class="number-pad"></div>
@@ -30,7 +30,7 @@
<div class="totals-section">
<div class="totals"></div>
</div>
- <div class="submit-order-btn">Complete Order</div>
+ <div class="submit-order-btn">${__("Complete Order")}</div>
</section>`
);
this.$component = this.wrapper.find('.payment-container');
@@ -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) {
@@ -545,12 +518,12 @@
this.$totals.html(
`<div class="col">
- <div class="total-label">Grand Total</div>
+ <div class="total-label">${__('Grand Total')}</div>
<div class="value">${format_currency(grand_total, currency)}</div>
</div>
<div class="seperator-y"></div>
<div class="col">
- <div class="total-label">Paid Amount</div>
+ <div class="total-label">${__('Paid Amount')}</div>
<div class="value">${format_currency(paid_amount, currency)}</div>
</div>
<div class="seperator-y"></div>
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py
index 78aaa49..a75108e 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.py
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.py
@@ -1,12 +1,13 @@
# 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.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):
diff --git a/erpnext/healthcare/doctype/lab_test_template/__init__.py b/erpnext/selling/print_format_field_template/__init__.py
similarity index 100%
copy from erpnext/healthcare/doctype/lab_test_template/__init__.py
copy to erpnext/selling/print_format_field_template/__init__.py
diff --git a/erpnext/healthcare/doctype/diagnosis/__init__.py b/erpnext/selling/print_format_field_template/quotation_taxes/__init__.py
similarity index 100%
copy from erpnext/healthcare/doctype/diagnosis/__init__.py
copy to erpnext/selling/print_format_field_template/quotation_taxes/__init__.py
diff --git a/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json b/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json
new file mode 100644
index 0000000..eaa17ce
--- /dev/null
+++ b/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 15:48:56.416449",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Quotation",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:11:33.553722",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Quotation Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/selling/print_format_field_template/sales_order_taxes/__init__.py
similarity index 100%
copy from erpnext/buying/doctype/supplier_item_group/__init__.py
copy to erpnext/selling/print_format_field_template/sales_order_taxes/__init__.py
diff --git a/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json b/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json
new file mode 100644
index 0000000..2aacb44
--- /dev/null
+++ b/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:04:24.443076",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Sales Order",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:04:24.443076",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.py b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
index f295333..915f8dc 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
@@ -1,11 +1,8 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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
field_map = {
"Contact": [ "first_name", "last_name", "phone", "mobile_no", "email_id", "is_primary_contact" ],
@@ -67,7 +64,7 @@
party_details = get_party_details(party_type, party_list, "Address", party_details)
party_details = get_party_details(party_type, party_list, "Contact", party_details)
- for party, details in iteritems(party_details):
+ for party, details in party_details.items():
addresses = details.get("address", [])
contacts = details.get("contact", [])
if not any([addresses, contacts]):
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 5523bad..e702a51 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
@@ -1,10 +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
from frappe.utils import flt
+
def execute(filters=None):
if not filters: filters = {}
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..2426cbb 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
@@ -1,12 +1,14 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..777b02c 100644
--- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
+++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
@@ -1,11 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
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..e5f9354 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
@@ -1,13 +1,13 @@
# 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 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..d97e1c6 100644
--- a/erpnext/selling/report/inactive_customers/inactive_customers.py
+++ b/erpnext/selling/report/inactive_customers/inactive_customers.py
@@ -1,10 +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.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.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
index 1700fc7..4a245e1 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
@@ -1,12 +1,13 @@
# Copyright (c) 2019, 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
from frappe.utils.nestedset import get_descendants_of
+
def execute(filters=None):
filters = frappe._dict(filters or {})
if filters.from_date > filters.to_date:
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..01421e8 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
@@ -1,11 +1,12 @@
# 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 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..9c30afc 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
@@ -1,13 +1,16 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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.py b/erpnext/selling/report/quotation_trends/quotation_trends.py
index 968e2ff..047b090 100644
--- a/erpnext/selling/report/quotation_trends/quotation_trends.py
+++ b/erpnext/selling/report/quotation_trends/quotation_trends.py
@@ -1,10 +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
+
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.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
index d036a1c..83588c3 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.py
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -1,13 +1,14 @@
# 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 _, scrub
-from frappe.utils import getdate, flt, add_to_date, add_days
-from six import iteritems
+from frappe.utils import add_days, add_to_date, flt, getdate
+
from erpnext.accounts.utils import get_fiscal_year
+
def execute(filters=None):
return Analytics(filters).run()
@@ -224,7 +225,7 @@
self.data = []
self.get_periodic_data()
- for entity, period_data in iteritems(self.entity_periodic_data):
+ for entity, period_data in self.entity_periodic_data.items():
row = {
"entity": entity,
"entity_name": self.entity_names.get(entity) if hasattr(self, 'entity_names') else None
@@ -293,7 +294,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..8ffc5d6 100644
--- a/erpnext/selling/report/sales_analytics/test_analytics.py
+++ b/erpnext/selling/report/sales_analytics/test_analytics.py
@@ -1,12 +1,14 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 00dcd69..82e5d0c 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -1,11 +1,13 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-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 +72,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')
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..5a71171 100644
--- a/erpnext/selling/report/sales_order_trends/sales_order_trends.py
+++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.py
@@ -1,10 +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
+
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 2c49d51..b775907 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
@@ -1,10 +1,9 @@
# 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 msgprint, _
-from frappe.utils import flt
+from frappe import _, msgprint
def execute(filters=None):
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 24ca666..a647eb4 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
@@ -1,13 +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
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 = []
@@ -44,18 +47,6 @@
if d.item_group not in item_groups:
item_groups.append(d.item_group)
- if item_groups:
- child_items = []
- for item_group in item_groups:
- if frappe.db.get_value("Item Group", {"name":item_group}, "is_group"):
- for child_item_group in frappe.get_all("Item Group", {"parent_item_group":item_group}):
- if child_item_group['name'] not in child_items:
- child_items.append(child_item_group['name'])
-
- for item in child_items:
- if item not in item_groups:
- item_groups.append(item)
-
date_field = ("transaction_date"
if filters.get('doctype') == "Sales Order" else "posting_date")
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 87ed5a8..f2b6a54 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
@@ -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
-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/sales_partner_transaction_summary/sales_partner_transaction_summary.py b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py
index f07293d..c64555b 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
@@ -1,10 +1,9 @@
# 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 msgprint, _
-from frappe.utils import flt
+from frappe import _, msgprint
def execute(filters=None):
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 9917d72..1542e31 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
@@ -1,10 +1,9 @@
# 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 msgprint, _
-from frappe.utils import flt
+from frappe import _, msgprint
def execute(filters=None):
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 ea9bbab..dda2466 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
@@ -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
-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/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..c621be8 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
@@ -1,12 +1,13 @@
# 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 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.py b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.py
index b1d89cc..eee2d42 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
@@ -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
-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..b7b4d3a 100644
--- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
+++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
@@ -1,11 +1,13 @@
# 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 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 2de57c8..e2e0db4 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -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}
}
});
}
@@ -90,10 +90,7 @@
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();
}
@@ -160,25 +157,19 @@
commission_rate() {
this.calculate_commission();
- refresh_field("total_commission");
}
total_commission() {
- if(this.frm.doc.base_net_total) {
- frappe.model.round_floats_in(this.frm.doc, ["base_net_total", "total_commission"]);
+ frappe.model.round_floats_in(this.frm.doc, ["amount_eligible_for_commission", "total_commission"]);
- if(this.frm.doc.base_net_total < this.frm.doc.total_commission) {
- var msg = (__("[Error]") + " " +
- __(frappe.meta.get_label(this.frm.doc.doctype, "total_commission",
- this.frm.doc.name)) + " > " +
- __(frappe.meta.get_label(this.frm.doc.doctype, "base_net_total", this.frm.doc.name)));
- frappe.msgprint(msg);
- throw msg;
- }
+ const { amount_eligible_for_commission } = this.frm.doc;
+ if(!amount_eligible_for_commission) return;
- this.frm.set_value("commission_rate",
- flt(this.frm.doc.total_commission * 100.0 / this.frm.doc.base_net_total));
- }
+ this.frm.set_value(
+ "commission_rate", flt(
+ this.frm.doc.total_commission * 100.0 / amount_eligible_for_commission
+ )
+ );
}
allocated_percentage(doc, cdt, cdn) {
@@ -188,7 +179,7 @@
sales_person.allocated_percentage = flt(sales_person.allocated_percentage,
precision("allocated_percentage", sales_person));
- sales_person.allocated_amount = flt(this.frm.doc.base_net_total *
+ sales_person.allocated_amount = flt(this.frm.doc.amount_eligible_for_commission *
sales_person.allocated_percentage / 100.0,
precision("allocated_amount", sales_person));
refresh_field(["allocated_amount"], sales_person);
@@ -209,8 +200,10 @@
var me = this;
var item = frappe.get_doc(cdt, cdn);
- if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) {
- return;
+ // check if serial nos entered are as much as qty in row
+ if (item.serial_no) {
+ let serial_nos = item.serial_no.split(`\n`).filter(sn => sn.trim()); // filter out whitespaces
+ if (item.qty === serial_nos.length) return;
}
if (item.serial_no && !item.batch_no) {
@@ -250,33 +243,49 @@
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
+ );
}
}
calculate_commission() {
- if(this.frm.fields_dict.commission_rate) {
- if(this.frm.doc.commission_rate > 100) {
- var msg = __(frappe.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) +
- " " + __("cannot be greater than 100");
- frappe.msgprint(msg);
- throw msg;
- }
+ if(!this.frm.fields_dict.commission_rate) return;
- this.frm.doc.total_commission = flt(this.frm.doc.base_net_total * this.frm.doc.commission_rate / 100.0,
- precision("total_commission"));
+ if(this.frm.doc.commission_rate > 100) {
+ this.frm.set_value("commission_rate", 100);
+ frappe.throw(`${__(frappe.meta.get_label(
+ this.frm.doc.doctype, "commission_rate", this.frm.doc.name
+ ))} ${__("cannot be greater than 100")}`);
}
+
+ this.frm.doc.amount_eligible_for_commission = this.frm.doc.items.reduce(
+ (sum, item) => item.grant_commission ? sum + item.base_net_amount : sum, 0
+ )
+
+ this.frm.doc.total_commission = flt(
+ this.frm.doc.amount_eligible_for_commission * this.frm.doc.commission_rate / 100.0,
+ precision("total_commission")
+ );
+
+ refresh_field(["amount_eligible_for_commission", "total_commission"]);
}
calculate_contribution() {
var me = this;
$.each(this.frm.doc.doctype.sales_team || [], function(i, sales_person) {
frappe.model.round_floats_in(sales_person);
- if(sales_person.allocated_percentage) {
- sales_person.allocated_amount = flt(
- me.frm.doc.base_net_total * sales_person.allocated_percentage / 100.0,
- precision("allocated_amount", sales_person));
- }
+ if (!sales_person.allocated_percentage) return;
+
+ sales_person.allocated_amount = flt(
+ me.frm.doc.amount_eligible_for_commission
+ * sales_person.allocated_percentage
+ / 100.0,
+ precision("allocated_amount", sales_person)
+ );
});
}
@@ -470,33 +479,37 @@
"reqd": 1
},
{
+ "fieldtype": "Table MultiSelect",
+ "label": __("Competitors"),
+ "fieldname": "competitors",
+ "options": "Competitor Detail"
+ },
+ {
"fieldtype": "Text",
"label": __("Detailed Reason"),
"fieldname": "detailed_reason"
},
],
primary_action: function() {
- var values = dialog.get_values();
- var reasons = values["lost_reason"];
- var detailed_reason = values["detailed_reason"];
+ let values = dialog.get_values();
frm.call({
doc: frm.doc,
method: 'declare_enquiry_lost',
args: {
- 'lost_reasons_list': reasons,
- 'detailed_reason': detailed_reason
+ 'lost_reasons_list': values.lost_reason,
+ 'competitors': values.competitors,
+ 'detailed_reason': values.detailed_reason
},
callback: function(r) {
dialog.hide();
frm.reload_doc();
},
});
- refresh_field("lost_reason");
},
primary_action_label: __('Declare Lost')
});
dialog.show();
}
-})
+})
\ No newline at end of file
diff --git a/erpnext/selling/workspace/retail/retail.json b/erpnext/selling/workspace/retail/retail.json
index 9d2e6ca..a851ace 100644
--- a/erpnext/selling/workspace/retail/retail.json
+++ b/erpnext/selling/workspace/retail/retail.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Point Of Sale\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings & Configurations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loyalty Program\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening & Closing\", \"col\": 4}}]",
"creation": "2020-03-02 17:18:32.505616",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "retail",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Retail",
"links": [
{
@@ -108,15 +101,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:01.840988",
+ "modified": "2021-08-05 12:16:01.840989",
"modified_by": "Administrator",
"module": "Selling",
"name": "Retail",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "Retail",
"roles": [],
diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json
index 345187f..db2e6ba 100644
--- a/erpnext/selling/workspace/selling/selling.json
+++ b/erpnext/selling/workspace/selling/selling.json
@@ -1,26 +1,18 @@
{
- "category": "",
"charts": [
{
"chart_name": "Sales Order Trends",
"label": "Sales Order Trends"
}
],
- "charts_label": "Selling ",
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Selling\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Sales Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Selling\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
"creation": "2020-01-28 11:49:12.092882",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "sell",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Selling",
"links": [
{
@@ -570,15 +562,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:01.990702",
+ "modified": "2021-08-05 12:16:01.990703",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling",
- "onboarding": "Selling",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
@@ -619,6 +608,5 @@
"type": "Dashboard"
}
],
- "shortcuts_label": "Quick Access",
"title": "Selling"
}
\ No newline at end of file
diff --git a/erpnext/setup/default_energy_point_rules.py b/erpnext/setup/default_energy_point_rules.py
index 8dbccc4..1ce06b8 100644
--- a/erpnext/setup/default_energy_point_rules.py
+++ b/erpnext/setup/default_energy_point_rules.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
from frappe import _
doctype_rule_map = {
diff --git a/erpnext/setup/default_success_action.py b/erpnext/setup/default_success_action.py
index 827839f..a1f48df 100644
--- a/erpnext/setup/default_success_action.py
+++ b/erpnext/setup/default_success_action.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
from frappe import _
doctype_list = [
diff --git a/erpnext/setup/doctype/__init__.py b/erpnext/setup/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/__init__.py
+++ b/erpnext/setup/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/authorization_control/__init__.py b/erpnext/setup/doctype/authorization_control/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/authorization_control/__init__.py
+++ b/erpnext/setup/doctype/authorization_control/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py
index fec5c7c..2a0d785 100644
--- a/erpnext/setup/doctype/authorization_control/authorization_control.py
+++ b/erpnext/setup/doctype/authorization_control/authorization_control.py
@@ -1,12 +1,14 @@
# 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 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/__init__.py b/erpnext/setup/doctype/authorization_rule/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/authorization_rule/__init__.py
+++ b/erpnext/setup/doctype/authorization_rule/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/authorization_rule/authorization_rule.py b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
index eb8e6eb..e07de3b 100644
--- a/erpnext/setup/doctype/authorization_rule/authorization_rule.py
+++ b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
@@ -1,13 +1,12 @@
# 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 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..7d3d5d4 100644
--- a/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
+++ b/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/setup/doctype/brand/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/brand/__init__.py
+++ b/erpnext/setup/doctype/brand/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/brand/brand.py b/erpnext/setup/doctype/brand/brand.py
index a8d1cf8..9b91b45 100644
--- a/erpnext/setup/doctype/brand/brand.py
+++ b/erpnext/setup/doctype/brand/brand.py
@@ -1,12 +1,13 @@
# 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 copy
+import frappe
from frappe.model.document import Document
+
class Brand(Document):
pass
diff --git a/erpnext/setup/doctype/brand/test_brand.py b/erpnext/setup/doctype/brand/test_brand.py
index 25ed86e..1c71448 100644
--- a/erpnext/setup/doctype/brand/test_brand.py
+++ b/erpnext/setup/doctype/brand/test_brand.py
@@ -1,7 +1,6 @@
# 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
+
test_records = frappe.get_test_records('Brand')
diff --git a/erpnext/setup/doctype/company/__init__.py b/erpnext/setup/doctype/company/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/company/__init__.py
+++ b/erpnext/setup/doctype/company/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 56700af..91f60fb 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -12,6 +12,10 @@
}
});
}
+
+ frm.call('check_if_transactions_exist').then(r => {
+ frm.toggle_enable("default_currency", (!r.message));
+ });
},
setup: function(frm) {
erpnext.company.setup_queries(frm);
@@ -46,43 +50,6 @@
});
},
- change_abbreviation(frm) {
- var dialog = new frappe.ui.Dialog({
- title: "Replace Abbr",
- fields: [
- {"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr",
- "reqd": 1 },
- {"fieldtype": "Button", "label": "Update", "fieldname": "update"},
- ]
- });
-
- dialog.fields_dict.update.$input.click(function() {
- var args = dialog.get_values();
- if (!args) return;
- frappe.show_alert(__("Update in progress. It might take a while."));
- return frappe.call({
- method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr",
- args: {
- "company": frm.doc.name,
- "old": frm.doc.abbr,
- "new": args.new_abbr
- },
- callback: function(r) {
- if (r.exc) {
- frappe.msgprint(__("There were errors."));
- return;
- } else {
- frm.set_value("abbr", args.new_abbr);
- }
- dialog.hide();
- frm.refresh();
- },
- btn: this
- });
- });
- dialog.show();
- },
-
company_name: function(frm) {
if(frm.doc.__islocal) {
// add missing " " arg in split method
@@ -124,9 +91,6 @@
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Company'}
- frm.toggle_enable("default_currency", (frm.doc.__onload &&
- !frm.doc.__onload.transactions_exist));
-
if (frappe.perm.has_perm("Cost Center", 0, 'read')) {
frm.add_custom_button(__('Cost Centers'), function() {
frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name});
@@ -164,10 +128,6 @@
}, __('Manage'));
}
}
-
- frm.add_custom_button(__('Change Abbreviation'), () => {
- frm.trigger('change_abbreviation');
- }, __('Manage'));
}
erpnext.company.set_chart_of_accounts_options(frm.doc);
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index e4ee3ec..63d96bf 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -125,7 +125,8 @@
"label": "Abbr",
"oldfieldname": "abbr",
"oldfieldtype": "Data",
- "reqd": 1
+ "reqd": 1,
+ "set_only_once": 1
},
{
"bold": 1,
@@ -716,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",
@@ -731,15 +741,6 @@
"fieldname": "section_break_28",
"fieldtype": "Section Break",
"label": "Chart of Accounts"
- },
- {
- "fieldname": "hr_settings_section",
- "fieldtype": "Section Break",
- "label": "HR & Payroll Settings"
- },
- {
- "fieldname": "column_break_26",
- "fieldtype": "Column Break"
}
],
"icon": "fa fa-building",
@@ -747,10 +748,11 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
- "modified": "2021-07-12 11:27:06.353860",
+ "modified": "2021-10-04 12:09:25.833133",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
+ "naming_rule": "By fieldname",
"nsm_parent_field": "parent_company",
"owner": "Administrator",
"permissions": [
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 6dee2ad..e739739 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -1,31 +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 frappe, os, json
-from frappe import _
-from frappe.utils import get_timestamp
-from frappe.utils import cint, today, formatdate
+import json
+import os
+
+import frappe
import frappe.defaults
+from frappe import _
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 import cint, formatdate, get_timestamp, today
from frappe.utils.nestedset import NestedSet
-from past.builtins import cmp
-import functools
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'
def onload(self):
load_address_and_contact(self, "company")
- self.get("__onload")["transactions_exist"] = self.check_if_transactions_exist()
+ @frappe.whitelist()
def check_if_transactions_exist(self):
exists = False
for doctype in ["Sales Invoice", "Delivery Note", "Sales Order", "Quotation",
@@ -387,6 +385,7 @@
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)
@@ -397,44 +396,6 @@
if not frappe.db.get_value('GL Entry', {'company': self.name}):
frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name)
-@frappe.whitelist()
-def enqueue_replace_abbr(company, old, new):
- kwargs = dict(queue="long", company=company, old=old, new=new)
- frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs)
-
-
-@frappe.whitelist()
-def replace_abbr(company, old, new):
- new = new.strip()
- if not new:
- frappe.throw(_("Abbr can not be blank or space"))
-
- frappe.only_for("System Manager")
-
- def _rename_record(doc):
- parts = doc[0].rsplit(" - ", 1)
- if len(parts) == 1 or parts[1].lower() == old.lower():
- frappe.rename_doc(dt, doc[0], parts[0] + " - " + new, force=True)
-
- def _rename_records(dt):
- # rename is expensive so let's be economical with memory usage
- doc = (d for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company))
- for d in doc:
- _rename_record(d)
- try:
- frappe.db.auto_commit_on_many_writes = 1
- for dt in ["Warehouse", "Account", "Cost Center", "Department",
- "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
- _rename_records(dt)
- frappe.db.commit()
- frappe.db.set_value("Company", company, "abbr", new)
-
- except Exception:
- frappe.log_error(title=_('Abbreviation Rename Error'))
- finally:
- frappe.db.auto_commit_on_many_writes = 0
-
-
def get_name_with_abbr(name, company):
company_abbr = frappe.get_cached_value('Company', company, "abbr")
parts = name.split(" - ")
@@ -452,7 +413,7 @@
frappe.get_attr(module_name)(company, False)
except Exception as e:
frappe.log_error()
- frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(country)))
+ frappe.throw(_("Failed to setup defaults for country {0}. Please contact support.").format(frappe.bold(country)))
def update_company_current_month_sales(company):
@@ -479,8 +440,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")
@@ -619,7 +581,7 @@
return existing_address
if out:
- return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0]
+ return min(out, key=lambda x: x[1])[0] # find min by sort_key
else:
return None
diff --git a/erpnext/setup/doctype/company/company_dashboard.py b/erpnext/setup/doctype/company/company_dashboard.py
index 2d76028..7cb0b12 100644
--- a/erpnext/setup/doctype/company/company_dashboard.py
+++ b/erpnext/setup/doctype/company/company_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'graph': True,
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index 1b7fd4f..4ee9492 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -1,13 +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 json
+import unittest
import frappe
-import unittest
-import json
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"]
diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
index 6480f60..4191935 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
@@ -3,11 +3,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/__init__.py b/erpnext/setup/doctype/customer_group/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/customer_group/__init__.py
+++ b/erpnext/setup/doctype/customer_group/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py
index c06669b..5b91726 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.py
+++ b/erpnext/setup/doctype/customer_group/customer_group.py
@@ -1,12 +1,12 @@
# 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 import _
-
-
from frappe.utils.nestedset import NestedSet, get_root_of
+
+
class CustomerGroup(NestedSet):
nsm_parent_field = 'parent_customer_group'
def validate(self):
diff --git a/erpnext/setup/doctype/customer_group/test_customer_group.py b/erpnext/setup/doctype/customer_group/test_customer_group.py
index ec90b37..f02ae09 100644
--- a/erpnext/setup/doctype/customer_group/test_customer_group.py
+++ b/erpnext/setup/doctype/customer_group/test_customer_group.py
@@ -1,10 +1,10 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
test_ignore = ["Price List"]
import frappe
+
test_records = frappe.get_test_records('Customer Group')
diff --git a/erpnext/setup/doctype/email_digest/__init__.py b/erpnext/setup/doctype/email_digest/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/email_digest/__init__.py
+++ b/erpnext/setup/doctype/email_digest/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index 6fbd4cd..02f9156 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -1,20 +1,34 @@
# 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 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)
@@ -60,9 +74,6 @@
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 5451ee1..fbd2d94 100644
--- a/erpnext/setup/doctype/email_digest/quotes.py
+++ b/erpnext/setup/doctype/email_digest/quotes.py
@@ -1,7 +1,6 @@
-# -*- 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"),
diff --git a/erpnext/setup/doctype/email_digest/test_email_digest.py b/erpnext/setup/doctype/email_digest/test_email_digest.py
index afe693a..3fdf168 100644
--- a/erpnext/setup/doctype/email_digest/test_email_digest.py
+++ b/erpnext/setup/doctype/email_digest/test_email_digest.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Email Digest')
diff --git a/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py
index 968c51c..06bf627 100644
--- a/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py
+++ b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmailDigestRecipient(Document):
pass
diff --git a/erpnext/setup/doctype/global_defaults/__init__.py b/erpnext/setup/doctype/global_defaults/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/global_defaults/__init__.py
+++ b/erpnext/setup/doctype/global_defaults/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py
index a0ba1ef..f0b720a 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.py
@@ -1,12 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 +22,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..a9d62ad 100644
--- a/erpnext/setup/doctype/global_defaults/test_global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/test_global_defaults.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class TestGlobalDefaults(unittest.TestCase):
pass
diff --git a/erpnext/setup/doctype/item_group/__init__.py b/erpnext/setup/doctype/item_group/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/item_group/__init__.py
+++ b/erpnext/setup/doctype/item_group/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index c46b6cc..c94b346 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -1,20 +1,22 @@
# 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 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,11 +38,11 @@
self.parent_item_group = _('All Item Groups')
self.make_route()
+ self.validate_item_group_defaults()
def on_update(self):
NestedSet.on_update(self)
invalidate_cache_for(self)
- self.validate_name_with_item()
self.validate_one_root()
self.delete_child_item_groups_key()
@@ -64,10 +66,6 @@
WebsiteGenerator.on_trash(self)
self.delete_child_item_groups_key()
- def validate_name_with_item(self):
- if frappe.db.exists("Item", self.name):
- frappe.throw(frappe._("An item exists with same name ({0}), please change the item group name or rename the item").format(self.name), frappe.NameError)
-
def get_context(self, context):
context.show_search=True
context.page_length = cint(frappe.db.get_single_value('Products Settings', 'products_per_page')) or 6
@@ -96,7 +94,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 +129,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/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..f6e9ed4 100644
--- a/erpnext/setup/doctype/item_group/test_item_group.py
+++ b/erpnext/setup/doctype/item_group/test_item_group.py
@@ -1,11 +1,18 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/setup/doctype/naming_series/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/naming_series/__init__.py
+++ b/erpnext/setup/doctype/naming_series/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py
index c1f9433..986b4e8 100644
--- a/erpnext/setup/doctype/naming_series/naming_series.py
+++ b/erpnext/setup/doctype/naming_series/naming_series.py
@@ -1,16 +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
-
-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 +78,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 +138,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()
@@ -180,11 +180,11 @@
prefix = parse_naming_series(parts)
return prefix
-def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True):
+def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True, make_mandatory=1):
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
if naming_series:
make_property_setter(doctype, "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False)
- make_property_setter(doctype, "naming_series", "reqd", 1, "Check", validate_fields_for_doctype=False)
+ make_property_setter(doctype, "naming_series", "reqd", make_mandatory, "Check", validate_fields_for_doctype=False)
# set values for mandatory
try:
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..d0d2946 100644
--- a/erpnext/setup/doctype/party_type/party_type.py
+++ b/erpnext/setup/doctype/party_type/party_type.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# 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..a9a3db8 100644
--- a/erpnext/setup/doctype/party_type/test_party_type.py
+++ b/erpnext/setup/doctype/party_type/test_party_type.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/setup/doctype/print_heading/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/print_heading/__init__.py
+++ b/erpnext/setup/doctype/print_heading/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/print_heading/print_heading.py b/erpnext/setup/doctype/print_heading/print_heading.py
index 3d5cd2d..3a2f15f 100644
--- a/erpnext/setup/doctype/print_heading/print_heading.py
+++ b/erpnext/setup/doctype/print_heading/print_heading.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class PrintHeading(Document):
pass
diff --git a/erpnext/setup/doctype/print_heading/test_print_heading.py b/erpnext/setup/doctype/print_heading/test_print_heading.py
index b2be2e3..04de08d 100644
--- a/erpnext/setup/doctype/print_heading/test_print_heading.py
+++ b/erpnext/setup/doctype/print_heading/test_print_heading.py
@@ -1,7 +1,6 @@
# 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
+
test_records = frappe.get_test_records('Print Heading')
diff --git a/erpnext/setup/doctype/quotation_lost_reason/__init__.py b/erpnext/setup/doctype/quotation_lost_reason/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/quotation_lost_reason/__init__.py
+++ b/erpnext/setup/doctype/quotation_lost_reason/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 42c5a5a..651705c 100644
--- a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py
+++ b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class QuotationLostReason(Document):
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 f6b30b6..9330ba8 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
@@ -1,7 +1,6 @@
# 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
+
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..dda64e9 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QuotationLostReasonDetail(Document):
pass
diff --git a/erpnext/setup/doctype/sales_partner/__init__.py b/erpnext/setup/doctype/sales_partner/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/sales_partner/__init__.py
+++ b/erpnext/setup/doctype/sales_partner/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py
index 675f9ca..d2ec49d 100644
--- a/erpnext/setup/doctype/sales_partner/sales_partner.py
+++ b/erpnext/setup/doctype/sales_partner/sales_partner.py
@@ -1,11 +1,12 @@
# 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.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..80ef368 100644
--- a/erpnext/setup/doctype/sales_partner/test_sales_partner.py
+++ b/erpnext/setup/doctype/sales_partner/test_sales_partner.py
@@ -1,9 +1,8 @@
# 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
+
test_records = frappe.get_test_records('Sales Partner')
test_ignore = ["Item Group"]
diff --git a/erpnext/setup/doctype/sales_person/__init__.py b/erpnext/setup/doctype/sales_person/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/sales_person/__init__.py
+++ b/erpnext/setup/doctype/sales_person/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py
index 19c2e5b..b79a566 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.py
+++ b/erpnext/setup/doctype/sales_person/sales_person.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
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 662008e..e946406 100644
--- a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
+++ b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/setup/doctype/sales_person/test_sales_person.py b/erpnext/setup/doctype/sales_person/test_sales_person.py
index 8313cb4..786d2ca 100644
--- a/erpnext/setup/doctype/sales_person/test_sales_person.py
+++ b/erpnext/setup/doctype/sales_person/test_sales_person.py
@@ -1,10 +1,10 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
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/__init__.py b/erpnext/setup/doctype/supplier_group/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/supplier_group/__init__.py
+++ b/erpnext/setup/doctype/supplier_group/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.py b/erpnext/setup/doctype/supplier_group/supplier_group.py
index 9d84f90..381e125 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.py
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.py
@@ -1,11 +1,11 @@
-# -*- 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.utils.nestedset import NestedSet, get_root_of
+
class SupplierGroup(NestedSet):
nsm_parent_field = 'parent_supplier_group'
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..283b3bf 100644
--- a/erpnext/setup/doctype/supplier_group/test_supplier_group.py
+++ b/erpnext/setup/doctype/supplier_group/test_supplier_group.py
@@ -1,7 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import frappe
+
test_records = frappe.get_test_records('Supplier Group')
diff --git a/erpnext/setup/doctype/target_detail/__init__.py b/erpnext/setup/doctype/target_detail/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/target_detail/__init__.py
+++ b/erpnext/setup/doctype/target_detail/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/target_detail/target_detail.py b/erpnext/setup/doctype/target_detail/target_detail.py
index 633be45..e27f5b6 100644
--- a/erpnext/setup/doctype/target_detail/target_detail.py
+++ b/erpnext/setup/doctype/target_detail/target_detail.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class TargetDetail(Document):
pass
diff --git a/erpnext/setup/doctype/terms_and_conditions/__init__.py b/erpnext/setup/doctype/terms_and_conditions/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/terms_and_conditions/__init__.py
+++ b/erpnext/setup/doctype/terms_and_conditions/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 5b00ccb..658f286 100644
--- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py
+++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py
@@ -1,15 +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 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):
@@ -20,7 +20,7 @@
@frappe.whitelist()
def get_terms_and_conditions(template_name, doc):
- if isinstance(doc, string_types):
+ if isinstance(doc, str):
doc = json.loads(doc)
terms_and_conditions = frappe.get_doc("Terms and Conditions", template_name)
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..ca9e6c1 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
@@ -1,7 +1,6 @@
# 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
+
test_records = frappe.get_test_records('Terms and Conditions')
diff --git a/erpnext/setup/doctype/territory/__init__.py b/erpnext/setup/doctype/territory/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/territory/__init__.py
+++ b/erpnext/setup/doctype/territory/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py
index 7eefe77..4c47d82 100644
--- a/erpnext/setup/doctype/territory/territory.py
+++ b/erpnext/setup/doctype/territory/territory.py
@@ -1,13 +1,13 @@
# 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 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'
diff --git a/erpnext/setup/doctype/territory/test_territory.py b/erpnext/setup/doctype/territory/test_territory.py
index efe00c5..a18b7bf 100644
--- a/erpnext/setup/doctype/territory/test_territory.py
+++ b/erpnext/setup/doctype/territory/test_territory.py
@@ -1,9 +1,8 @@
# 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
+
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 933a8c3..095c3d0 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestTransactionDeletionRecord(unittest.TestCase):
def setUp(self):
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..83ce042 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# 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_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
index 2176cb1..92ca8a2 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
@@ -1,10 +1,10 @@
-# -*- 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 TransactionDeletionRecordItem(Document):
pass
diff --git a/erpnext/setup/doctype/uom/__init__.py b/erpnext/setup/doctype/uom/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/doctype/uom/__init__.py
+++ b/erpnext/setup/doctype/uom/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/doctype/uom/test_uom.py b/erpnext/setup/doctype/uom/test_uom.py
index 330d303..feb4329 100644
--- a/erpnext/setup/doctype/uom/test_uom.py
+++ b/erpnext/setup/doctype/uom/test_uom.py
@@ -1,7 +1,6 @@
# 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
+
test_records = frappe.get_test_records('UOM')
diff --git a/erpnext/setup/doctype/uom/uom.json b/erpnext/setup/doctype/uom/uom.json
index 3a4e7f6..844a11f 100644
--- a/erpnext/setup/doctype/uom/uom.json
+++ b/erpnext/setup/doctype/uom/uom.json
@@ -1,164 +1,82 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:uom_name",
- "beta": 0,
"creation": "2013-01-10 16:34:24",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
- "editable_grid": 0,
+ "engine": "InnoDB",
+ "field_order": [
+ "enabled",
+ "uom_name",
+ "must_be_whole_number"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "uom_name",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "UOM Name",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "uom_name",
"oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
"reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
"unique": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "0",
"description": "Check this to disallow fractions. (for Nos)",
"fieldname": "must_be_whole_number",
"fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Must be Whole Number",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Must be Whole Number"
+ },
+ {
+ "default": "1",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "label": "Enabled"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
"icon": "fa fa-compass",
"idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-08-29 06:35:56.143361",
+ "links": [],
+ "modified": "2021-10-18 14:07:43.722144",
"modified_by": "Administrator",
"module": "Setup",
"name": "UOM",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
"import": 1,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Item Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
"email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
- "role": "Stock Manager",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "role": "Stock Manager"
},
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
"email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
- "role": "Stock User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "role": "Stock User"
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
"show_name_in_global_search": 1,
- "sort_order": "ASC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "sort_field": "modified",
+ "sort_order": "ASC"
}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/uom/uom.py b/erpnext/setup/doctype/uom/uom.py
index 404b84b..ddb512a 100644
--- a/erpnext/setup/doctype/uom/uom.py
+++ b/erpnext/setup/doctype/uom/uom.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class UOM(Document):
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..5683b5b 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..12642fe 100644
--- a/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.py
+++ b/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.py
@@ -1,9 +1,9 @@
-# -*- 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 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 e416b50..87fcb98 100644
--- a/erpnext/setup/doctype/website_item_group/website_item_group.py
+++ b/erpnext/setup/doctype/website_item_group/website_item_group.py
@@ -3,10 +3,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
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index bbee74c..86c9b3f 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -1,18 +1,18 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-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 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 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 six import iteritems
+
+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>"""
@@ -172,12 +172,12 @@
user_types = get_user_types_data()
user_type_limit = {}
- for user_type, data in iteritems(user_types):
+ for user_type, data in user_types.items():
user_type_limit.setdefault(frappe.scrub(user_type), 10)
update_site_config('user_type_doctype_limit', user_type_limit)
- for user_type, data in iteritems(user_types):
+ for user_type, data in user_types.items():
create_custom_role(data)
create_user_type(user_type, data)
@@ -227,7 +227,7 @@
doc.save(ignore_permissions=True)
def create_role_permissions_for_doctype(doc, data):
- for doctype, perms in iteritems(data.get('doctypes')):
+ for doctype, perms in data.get('doctypes').items():
args = {'document_type': doctype}
for perm in perms:
args[perm] = 1
diff --git a/erpnext/setup/page/__init__.py b/erpnext/setup/page/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/setup/page/__init__.py
+++ b/erpnext/setup/page/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index 34af093..14b7951 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -1195,7 +1195,7 @@
"*": {
"item_tax_templates": [
{
- "title": "GST 9%",
+ "title": "GST 18%",
"taxes": [
{
"tax_type": {
@@ -2116,6 +2116,10 @@
},
"Saudi Arabia": {
+ "KSA VAT 15%": {
+ "account_name": "VAT 15%",
+ "tax_rate": 15.00
+ },
"KSA VAT 5%": {
"account_name": "VAT 5%",
"tax_rate": 5.00
diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py
index 9ce64eb..6cb15b2 100644
--- a/erpnext/setup/setup_wizard/data/dashboard_charts.py
+++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py
@@ -1,8 +1,8 @@
-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..ecd8b00 100644
--- a/erpnext/setup/setup_wizard/data/industry_type.py
+++ b/erpnext/setup/setup_wizard/data/industry_type.py
@@ -1,6 +1,6 @@
-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 4833d93..358b921 100644
--- a/erpnext/setup/setup_wizard/operations/company_setup.py
+++ b/erpnext/setup/setup_wizard/operations/company_setup.py
@@ -1,12 +1,10 @@
# 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 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')):
diff --git a/erpnext/setup/setup_wizard/operations/default_website.py b/erpnext/setup/setup_wizard/operations/default_website.py
index 38b5c14..c11910b 100644
--- a/erpnext/setup/setup_wizard/operations/default_website.py
+++ b/erpnext/setup/setup_wizard/operations/default_website.py
@@ -1,12 +1,12 @@
# 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 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..e4b1fa2 100644
--- a/erpnext/setup/setup_wizard/operations/defaults_setup.py
+++ b/erpnext/setup/setup_wizard/operations/defaults_setup.py
@@ -1,7 +1,6 @@
# 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 import _
from frappe.utils import cstr, getdate
@@ -62,6 +61,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..98f9119 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.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, 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",
@@ -198,7 +201,6 @@
{'doctype': "Party Type", "party_type": "Student", "account_type": "Receivable"},
{'doctype': "Party Type", "party_type": "Donor", "account_type": "Receivable"},
- {'doctype': "Opportunity Type", "name": "Hub"},
{'doctype': "Opportunity Type", "name": _("Sales")},
{'doctype': "Opportunity Type", "name": _("Support")},
{'doctype': "Opportunity Type", "name": _("Maintenance")},
@@ -260,16 +262,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
@@ -291,7 +303,6 @@
def update_selling_defaults():
selling_settings = frappe.get_doc("Selling Settings")
- selling_settings.set_default_customer_group_and_territory()
selling_settings.cust_master_name = "Customer Name"
selling_settings.so_required = "No"
selling_settings.dn_required = "No"
@@ -313,6 +324,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 c6d9f08..1685994 100644
--- a/erpnext/setup/setup_wizard/operations/sample_data.py
+++ b/erpnext/setup/setup_wizard/operations/sample_data.py
@@ -1,13 +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 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
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index faa25df..289ffa5 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -1,7 +1,6 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import os
import json
@@ -192,7 +191,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 +246,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..c9ed184 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -1,12 +1,14 @@
# 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 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 +108,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..f1ec50af 100644
--- a/erpnext/setup/setup_wizard/utils.py
+++ b/erpnext/setup/setup_wizard/utils.py
@@ -1,8 +1,8 @@
-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..cad4c54 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -1,13 +1,14 @@
# 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 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`
@@ -52,6 +53,7 @@
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 0)
enable_all_roles_and_domains()
+ set_defaults_for_tests()
frappe.db.commit()
@@ -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
@@ -126,6 +128,14 @@
[d.name for d in domains])
add_all_roles_to('Administrator')
+def set_defaults_for_tests():
+ from frappe.utils.nestedset import get_root_of
+
+ selling_settings = frappe.get_single("Selling Settings")
+ selling_settings.customer_group = get_root_of("Customer Group")
+ selling_settings.territory = get_root_of("Territory")
+ selling_settings.save()
+
def insert_record(records):
for r in records:
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index ef4b050..e47837f 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -1,31 +1,21 @@
{
- "category": "",
"charts": [],
- "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Projects Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"HR Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Selling Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Buying Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Support Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Shopping Cart Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Portal Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Manufacturing Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Education Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Hotel Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Domain Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Products Settings\", \"col\": 4}}]",
+ "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Projects Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"HR Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Selling Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Buying Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Support Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Shopping Cart Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Portal Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Domain Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Products Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Naming Series\",\"col\":4}}]",
"creation": "2020-03-12 14:47:51.166455",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "setting",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "ERPNext Settings",
"links": [],
- "modified": "2021-08-05 12:15:59.052327",
+ "modified": "2021-11-05 21:32:55.323591",
"modified_by": "Administrator",
"module": "Setup",
"name": "ERPNext Settings",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
@@ -38,6 +28,14 @@
"type": "DocType"
},
{
+ "color": "Grey",
+ "doc_view": "",
+ "icon": "dot-horizontal",
+ "label": "Naming Series",
+ "link_to": "Naming Series",
+ "type": "DocType"
+ },
+ {
"icon": "accounting",
"label": "Accounts Settings",
"link_to": "Accounts Settings",
@@ -125,6 +123,13 @@
"label": "Products Settings",
"link_to": "Products Settings",
"type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "icon": "crm",
+ "label": "CRM Settings",
+ "link_to": "CRM Settings",
+ "type": "DocType"
}
],
"title": "ERPNext Settings"
diff --git a/erpnext/setup/workspace/home/home.json b/erpnext/setup/workspace/home/home.json
index cc9569f..4e1ccf9 100644
--- a/erpnext/setup/workspace/home/home.json
+++ b/erpnext/setup/workspace/home/home.json
@@ -1,20 +1,13 @@
{
- "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,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "getting-started",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Home",
"links": [
{
@@ -278,15 +271,12 @@
"type": "Link"
}
],
- "modified": "2021-08-10 15:33:20.704740",
+ "modified": "2021-08-10 15:33:20.704741",
"modified_by": "Administrator",
"module": "Setup",
"name": "Home",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index e9f4bd5..ebbe233 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -1,17 +1,20 @@
# 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 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):
@@ -191,7 +194,9 @@
def create_lead_for_item_inquiry(lead, subject, message):
lead = frappe.parse_json(lead)
lead_doc = frappe.new_doc('Lead')
- lead_doc.update(lead)
+ for fieldname in ("lead_name", "company_name", "email_id", "phone"):
+ lead_doc.set(fieldname, lead.get(fieldname))
+
lead_doc.set('lead_owner', '')
if not frappe.db.exists('Lead Source', 'Product Inquiry'):
@@ -199,6 +204,7 @@
'doctype': 'Lead Source',
'source_name' : 'Product Inquiry'
}).insert(ignore_permissions=True)
+
lead_doc.set('source', 'Product Inquiry')
try:
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 efed196..4a75599 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
@@ -3,12 +3,12 @@
# 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 _
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
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 9965e1a..c3809b3 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
@@ -3,10 +3,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):
@@ -38,7 +43,6 @@
def test_tax_rule_validation(self):
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
- frappe.db.commit()
cart_settings = self.get_cart_settings()
cart_settings.enabled = 1
diff --git a/erpnext/shopping_cart/filters.py b/erpnext/shopping_cart/filters.py
index 7dfa09e..ef0badc 100644
--- a/erpnext/shopping_cart/filters.py
+++ b/erpnext/shopping_cart/filters.py
@@ -1,9 +1,9 @@
# Copyright (c) 2020, 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 _dict
+
class ProductFiltersBuilder:
def __init__(self, item_group=None):
@@ -55,37 +55,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 6c9e531..977f12f 100644
--- a/erpnext/shopping_cart/product_info.py
+++ b/erpnext/shopping_cart/product_info.py
@@ -1,13 +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
+
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):
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 9f674dc..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"
diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py
index ac61aeb..60c220a 100644
--- a/erpnext/shopping_cart/test_shopping_cart.py
+++ b/erpnext/shopping_cart/test_shopping_cart.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 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 0e1466f..5f0c792 100644
--- a/erpnext/shopping_cart/utils.py
+++ b/erpnext/shopping_cart/utils.py
@@ -1,11 +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 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
@@ -15,10 +15,19 @@
return False
def set_cart_count(login_manager):
- role, parties = check_customer_or_supplier()
- if role == 'Supplier': return
+ # since this is run only on hooks login event
+ # make sure user is already a customer
+ # before trying to set cart count
+ user_is_customer = is_customer()
+ if not user_is_customer:
+ return
+
if show_cart_count():
from erpnext.shopping_cart.cart import set_cart_count
+
+ # set_cart_count will try to fetch existing cart quotation
+ # or create one if non existent (and create a customer too)
+ # cart count is calculated from this quotation's items
set_cart_count()
def clear_cart_count(login_manager):
@@ -29,13 +38,13 @@
cart_enabled = is_cart_enabled()
context["shopping_cart_enabled"] = cart_enabled
-def check_customer_or_supplier():
- if frappe.session.user:
+def is_customer():
+ if frappe.session.user and frappe.session.user != "Guest":
contact_name = frappe.get_value("Contact", {"email_id": frappe.session.user})
if contact_name:
contact = frappe.get_doc('Contact', contact_name)
for link in contact.links:
- if link.link_doctype in ('Customer', 'Supplier'):
- return link.link_doctype, link.link_name
+ if link.link_doctype == 'Customer':
+ return True
- return 'Customer', None
+ return False
diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html
index 1e3d0d0..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>
diff --git a/erpnext/startup/__init__.py b/erpnext/startup/__init__.py
index deef4ba..dd1b401 100644
--- a/erpnext/startup/__init__.py
+++ b/erpnext/startup/__init__.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
# ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Frappe Technologies Pvt Ltd
#
@@ -17,8 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# default settings that can be made for a user.
-from __future__ import unicode_literals
-
product_name = "ERPNext"
user_defaults = {
diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py
index 2b80fb8..0da45a5 100644
--- a/erpnext/startup/boot.py
+++ b/erpnext/startup/boot.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 cint
+
def boot_session(bootinfo):
"""boot session - send website info if guest"""
@@ -21,7 +22,7 @@
'customer_group')
bootinfo.sysdefaults.allow_stale = cint(frappe.db.get_single_value('Accounts Settings',
'allow_stale'))
- bootinfo.sysdefaults.quotation_valid_till = cint(frappe.db.get_single_value('Selling Settings',
+ bootinfo.sysdefaults.quotation_valid_till = cint(frappe.db.get_single_value('CRM Settings',
'default_valid_till'))
# if no company, show a dialog box to create a new company
diff --git a/erpnext/startup/filters.py b/erpnext/startup/filters.py
index 9821016..4fd6431 100644
--- a/erpnext/startup/filters.py
+++ b/erpnext/startup/filters.py
@@ -1,6 +1,3 @@
-
-import frappe
-
def get_filters_config():
filters_config = {
"fiscal year": {
diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py
index a89435d..8ae70d2 100644
--- a/erpnext/startup/leaderboard.py
+++ b/erpnext/startup/leaderboard.py
@@ -1,8 +1,7 @@
-
-from __future__ import unicode_literals, print_function
import frappe
from frappe.utils import cint
+
def get_leaderboards():
leaderboards = {
"Customer": {
diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py
index 8e880dc..0965ead 100644
--- a/erpnext/startup/notifications.py
+++ b/erpnext/startup/notifications.py
@@ -1,9 +1,10 @@
# 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
+
def get_notification_config():
notifications = { "for_doctype":
{
diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py
index 1eaf738..65b48bf 100644
--- a/erpnext/startup/report_data_map.py
+++ b/erpnext/startup/report_data_map.py
@@ -1,7 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
# mappings for table dumps
# "remember to add indexes!"
diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py
index 283f7d5..e8b2804 100644
--- a/erpnext/stock/__init__.py
+++ b/erpnext/stock/__init__.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
import frappe
from frappe import _
diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py
index 45e6628..57d78a2 100644
--- a/erpnext/stock/dashboard/item_dashboard.py
+++ b/erpnext/stock/dashboard/item_dashboard.py
@@ -1,8 +1,7 @@
-from __future__ import unicode_literals
-
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 70b030e..c0666cf 100644
--- a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
+++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
@@ -1,11 +1,10 @@
-from __future__ import unicode_literals
-
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"):
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 2258532..d835420 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
@@ -1,12 +1,14 @@
# 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
+
+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,
diff --git a/erpnext/stock/doctype/__init__.py b/erpnext/stock/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/__init__.py
+++ b/erpnext/stock/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/batch/__init__.py b/erpnext/stock/doctype/batch/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/batch/__init__.py
+++ b/erpnext/stock/doctype/batch/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index b37ae3f..fdefd24 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -1,16 +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
-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
+
class UnableToSelectBatchError(frappe.ValidationError):
pass
@@ -62,7 +61,7 @@
:param prefix: Naming series prefix gotten from Stock Settings
:return: The derived key. If no prefix is given, an empty string is returned
"""
- if not text_type(prefix):
+ if not str(prefix):
return ''
else:
return prefix.upper() + '.#####'
diff --git a/erpnext/stock/doctype/batch/batch_dashboard.py b/erpnext/stock/doctype/batch/batch_dashboard.py
index eb6a97e..725365b 100644
--- a/erpnext/stock/doctype/batch/batch_dashboard.py
+++ b/erpnext/stock/doctype/batch/batch_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index a85a022..0a663c2 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -1,17 +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 frappe
from frappe.exceptions import ValidationError
-import unittest
-
-from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
from frappe.utils import cint, flt
-from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
-from erpnext.stock.get_item_details import get_item_details
-class TestBatch(unittest.TestCase):
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
+from erpnext.stock.get_item_details import get_item_details
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestBatch(ERPNextTestCase):
def test_item_has_batch_enabled(self):
self.assertRaises(ValidationError, frappe.get_doc({
"doctype": "Batch",
diff --git a/erpnext/stock/doctype/bin/__init__.py b/erpnext/stock/doctype/bin/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/bin/__init__.py
+++ b/erpnext/stock/doctype/bin/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 4364201..a33134b 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -1,11 +1,13 @@
# 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 flt, nowdate
-import frappe.defaults
from frappe.model.document import Document
+from frappe.query_builder import Case
+from frappe.query_builder.functions import Coalesce, Sum
+from frappe.utils import flt
+
class Bin(Document):
def before_save(self):
@@ -13,85 +15,48 @@
self.stock_uom = frappe.get_cached_value('Item', self.item_code, 'stock_uom')
self.set_projected_qty()
- def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
- '''Called from erpnext.stock.utils.update_bin'''
- self.update_qty(args)
-
- if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
- from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
-
- if not args.get("posting_date"):
- args["posting_date"] = nowdate()
-
- if args.get("is_cancelled") and via_landed_cost_voucher:
- return
-
- # Reposts only current voucher SL Entries
- # Updates valuation rate, stock value, stock queue for current transaction
- update_entries_after({
- "item_code": self.item_code,
- "warehouse": self.warehouse,
- "posting_date": args.get("posting_date"),
- "posting_time": args.get("posting_time"),
- "voucher_type": args.get("voucher_type"),
- "voucher_no": args.get("voucher_no"),
- "sle_id": args.name,
- "creation": args.creation
- }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
-
- # update qty in future ale and Validate negative qty
- update_qty_in_future_sle(args, allow_negative_stock)
-
-
- def update_qty(self, args):
- # update the stock values (for current quantities)
- if args.get("voucher_type")=="Stock Reconciliation":
- self.actual_qty = args.get("qty_after_transaction")
- else:
- self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
-
- self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
- self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
- self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
- self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
-
- self.set_projected_qty()
- self.db_update()
-
def set_projected_qty(self):
self.projected_qty = (flt(self.actual_qty) + flt(self.ordered_qty)
+ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
- flt(self.reserved_qty_for_production) - flt(self.reserved_qty_for_sub_contract))
def get_first_sle(self):
- sle = frappe.db.sql("""
- select * from `tabStock Ledger Entry`
- where item_code = %s
- and warehouse = %s
- order by timestamp(posting_date, posting_time) asc, creation asc
- limit 1
- """, (self.item_code, self.warehouse), as_dict=1)
- return sle and sle[0] or None
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+ first_sle = (
+ frappe.qb.from_(sle)
+ .select("*")
+ .where((sle.item_code == self.item_code) & (sle.warehouse == self.warehouse))
+ .orderby(sle.posting_date, sle.posting_time, sle.creation)
+ .limit(1)
+ ).run(as_dict=True)
+
+ return first_sle and first_sle[0] or None
def update_reserved_qty_for_production(self):
'''Update qty reserved for production from Production Item tables
in open work orders'''
- self.reserved_qty_for_production = frappe.db.sql('''
- SELECT
- CASE WHEN ifnull(skip_transfer, 0) = 0 THEN
- SUM(item.required_qty - item.transferred_qty)
- ELSE
- SUM(item.required_qty - item.consumed_qty)
- END
- FROM `tabWork Order` pro, `tabWork Order Item` item
- WHERE
- item.item_code = %s
- and item.parent = pro.name
- and pro.docstatus = 1
- and item.source_warehouse = %s
- and pro.status not in ("Stopped", "Completed")
- and (item.required_qty > item.transferred_qty or item.required_qty > item.consumed_qty)
- ''', (self.item_code, self.warehouse))[0][0]
+
+ wo = frappe.qb.DocType("Work Order")
+ wo_item = frappe.qb.DocType("Work Order Item")
+
+ self.reserved_qty_for_production = (
+ frappe.qb
+ .from_(wo)
+ .from_(wo_item)
+ .select(Case()
+ .when(wo.skip_transfer == 0, Sum(wo_item.required_qty - wo_item.transferred_qty))
+ .else_(Sum(wo_item.required_qty - wo_item.consumed_qty))
+ )
+ .where(
+ (wo_item.item_code == self.item_code)
+ & (wo_item.parent == wo.name)
+ & (wo.docstatus == 1)
+ & (wo_item.source_warehouse == self.warehouse)
+ & (wo.status.notin(["Stopped", "Completed"]))
+ & ((wo_item.required_qty > wo_item.transferred_qty)
+ | (wo_item.required_qty > wo_item.consumed_qty))
+ )
+ ).run()[0][0] or 0.0
self.set_projected_qty()
@@ -100,36 +65,53 @@
def update_reserved_qty_for_sub_contracting(self):
#reserved qty
- reserved_qty_for_sub_contract = frappe.db.sql('''
- select ifnull(sum(itemsup.required_qty),0)
- from `tabPurchase Order` po, `tabPurchase Order Item Supplied` itemsup
- where
- itemsup.rm_item_code = %s
- and itemsup.parent = po.name
- and po.docstatus = 1
- and po.is_subcontracted = 'Yes'
- and po.status != 'Closed'
- and po.per_received < 100
- and itemsup.reserve_warehouse = %s''', (self.item_code, self.warehouse))[0][0]
- #Get Transferred Entries
- materials_transferred = frappe.db.sql("""
- select
- ifnull(sum(CASE WHEN se.is_return = 1 THEN (transfer_qty * -1) ELSE transfer_qty END),0)
- from
- `tabStock Entry` se, `tabStock Entry Detail` sed, `tabPurchase Order` po
- where
- se.docstatus=1
- and se.purpose='Send to Subcontractor'
- and ifnull(se.purchase_order, '') !=''
- and (sed.item_code = %(item)s or sed.original_item = %(item)s)
- and se.name = sed.parent
- and se.purchase_order = po.name
- and po.docstatus = 1
- and po.is_subcontracted = 'Yes'
- and po.status != 'Closed'
- and po.per_received < 100
- """, {'item': self.item_code})[0][0]
+ po = frappe.qb.DocType("Purchase Order")
+ supplied_item = frappe.qb.DocType("Purchase Order Item Supplied")
+
+ reserved_qty_for_sub_contract = (
+ frappe.qb
+ .from_(po)
+ .from_(supplied_item)
+ .select(Sum(Coalesce(supplied_item.required_qty, 0)))
+ .where(
+ (supplied_item.rm_item_code == self.item_code)
+ & (po.name == supplied_item.parent)
+ & (po.docstatus == 1)
+ & (po.is_subcontracted == "Yes")
+ & (po.status != "Closed")
+ & (po.per_received < 100)
+ & (supplied_item.reserve_warehouse == self.warehouse)
+ )
+ ).run()[0][0] or 0.0
+
+ se = frappe.qb.DocType("Stock Entry")
+ se_item = frappe.qb.DocType("Stock Entry Detail")
+
+ materials_transferred = (
+ frappe.qb
+ .from_(se)
+ .from_(se_item)
+ .from_(po)
+ .select(Sum(
+ Case()
+ .when(se.is_return == 1, se_item.transfer_qty * -1)
+ .else_(se_item.transfer_qty)
+ ))
+ .where(
+ (se.docstatus == 1)
+ & (se.purpose == "Send to Subcontractor")
+ & (Coalesce(se.purchase_order, "") != "")
+ & ((se_item.item_code == self.item_code)
+ | (se_item.original_item == self.item_code))
+ & (se.name == se_item.parent)
+ & (po.name == se.purchase_order)
+ & (po.docstatus == 1)
+ & (po.is_subcontracted == "Yes")
+ & (po.status != "Closed")
+ & (po.per_received < 100)
+ )
+ ).run()[0][0] or 0.0
if reserved_qty_for_sub_contract > materials_transferred:
reserved_qty_for_sub_contract = reserved_qty_for_sub_contract - materials_transferred
@@ -142,3 +124,45 @@
def on_doctype_update():
frappe.db.add_index("Bin", ["item_code", "warehouse"])
+
+
+def update_stock(bin_name, args, allow_negative_stock=False, via_landed_cost_voucher=False):
+ """WARNING: This function is deprecated. Inline this function instead of using it."""
+ from erpnext.stock.stock_ledger import repost_current_voucher
+
+ update_qty(bin_name, args)
+ repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher)
+
+def get_bin_details(bin_name):
+ return frappe.db.get_value('Bin', bin_name, ['actual_qty', 'ordered_qty',
+ 'reserved_qty', 'indented_qty', 'planned_qty', 'reserved_qty_for_production',
+ 'reserved_qty_for_sub_contract'], as_dict=1)
+
+def update_qty(bin_name, args):
+ bin_details = get_bin_details(bin_name)
+
+ # update the stock values (for current quantities)
+ if args.get("voucher_type")=="Stock Reconciliation":
+ actual_qty = args.get('qty_after_transaction')
+ else:
+ actual_qty = bin_details.actual_qty + flt(args.get("actual_qty"))
+
+ ordered_qty = flt(bin_details.ordered_qty) + flt(args.get("ordered_qty"))
+ reserved_qty = flt(bin_details.reserved_qty) + flt(args.get("reserved_qty"))
+ indented_qty = flt(bin_details.indented_qty) + flt(args.get("indented_qty"))
+ planned_qty = flt(bin_details.planned_qty) + flt(args.get("planned_qty"))
+
+
+ # compute projected qty
+ projected_qty = (flt(actual_qty) + flt(ordered_qty)
+ + flt(indented_qty) + flt(planned_qty) - flt(reserved_qty)
+ - flt(bin_details.reserved_qty_for_production) - flt(bin_details.reserved_qty_for_sub_contract))
+
+ frappe.db.set_value('Bin', bin_name, {
+ 'actual_qty': actual_qty,
+ 'ordered_qty': ordered_qty,
+ 'reserved_qty': reserved_qty,
+ 'indented_qty': indented_qty,
+ 'planned_qty': planned_qty,
+ 'projected_qty': projected_qty
+ })
diff --git a/erpnext/stock/doctype/bin/test_bin.py b/erpnext/stock/doctype/bin/test_bin.py
index fb32ce2..9c390d9 100644
--- a/erpnext/stock/doctype/bin/test_bin.py
+++ b/erpnext/stock/doctype/bin/test_bin.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..b52acb1 100644
--- a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.py
+++ b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.py
@@ -1,10 +1,9 @@
-# -*- 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.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..b3aa1f4 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/stock/doctype/delivery_note/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/delivery_note/__init__.py
+++ b/erpnext/stock/doctype/delivery_note/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 9581896..55a4c95 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -145,6 +145,7 @@
"sales_team_section_break",
"sales_partner",
"column_break7",
+ "amount_eligible_for_commission",
"commission_rate",
"total_commission",
"section_break1",
@@ -395,8 +396,7 @@
"fieldtype": "Link",
"label": "Billing Address Name",
"options": "Address",
- "print_hide": 1,
- "read_only": 1
+ "print_hide": 1
},
{
"fieldname": "tax_id",
@@ -543,6 +543,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "packed_items",
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"label": "Packing List",
@@ -551,6 +552,7 @@
"print_hide": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packed_items",
"fieldtype": "Table",
"label": "Packed Items",
@@ -1275,6 +1277,7 @@
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
"read_only": 1
@@ -1300,16 +1303,23 @@
"label": "Dispatch Address",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "amount_eligible_for_commission",
+ "fieldtype": "Currency",
+ "label": "Amount Eligible for Commission",
+ "read_only": 1
}
],
"icon": "fa fa-truck",
"idx": 146,
"is_submittable": 1,
"links": [],
- "modified": "2021-08-17 20:15:50.574966",
+ "modified": "2021-10-09 14:29:13.428984",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index f99a01b..70d48a4 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -1,20 +1,19 @@
# 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 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"
@@ -139,6 +138,7 @@
self.update_current_stock()
if not self.installation_status: self.installation_status = 'Not Installed'
+ self.reset_default_field_value("set_warehouse", "items", "warehouse")
def validate_with_previous_doc(self):
super(DeliveryNote, self).validate_with_previous_doc({
@@ -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 9db5db8..ca61a36 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'delivery_note',
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 91e7c00..4f89a19 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -2,30 +2,43 @@
# License: GNU General Public License v3. See license.txt
-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 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
+from erpnext.tests.utils import ERPNextTestCase
-class TestDeliveryNote(unittest.TestCase):
+class TestDeliveryNote(ERPNextTestCase):
def test_over_billing_against_dn(self):
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@@ -430,12 +443,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 +464,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 +513,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 +553,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 +595,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 +644,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 +795,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()
diff --git a/erpnext/stock/doctype/delivery_note_item/__init__.py b/erpnext/stock/doctype/delivery_note_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/delivery_note_item/__init__.py
+++ b/erpnext/stock/doctype/delivery_note_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..51c88be 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -49,6 +49,7 @@
"pricing_rules",
"stock_uom_rate",
"is_free_item",
+ "grant_commission",
"section_break_25",
"net_rate",
"net_amount",
@@ -468,7 +469,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,
@@ -753,13 +754,20 @@
"no_copy": 1,
"options": "currency",
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "grant_commission",
+ "fieldtype": "Check",
+ "label": "Grant Commission",
+ "read_only": 1
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-02-23 01:04:08.588104",
+ "modified": "2021-10-06 12:12:44.018872",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Item",
@@ -767,4 +775,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 8bd381a..cd0d725 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class DeliveryNoteItem(Document):
pass
diff --git a/erpnext/stock/doctype/delivery_settings/delivery_settings.py b/erpnext/stock/doctype/delivery_settings/delivery_settings.py
index 909efda..ab1ca80 100644
--- a/erpnext/stock/doctype/delivery_settings/delivery_settings.py
+++ b/erpnext/stock/doctype/delivery_settings/delivery_settings.py
@@ -1,10 +1,9 @@
-# -*- 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 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..37745dc 100644
--- a/erpnext/stock/doctype/delivery_settings/test_delivery_settings.py
+++ b/erpnext/stock/doctype/delivery_settings/test_delivery_settings.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
+
class 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..9da8bfa 100644
--- a/erpnext/stock/doctype/delivery_stop/delivery_stop.py
+++ b/erpnext/stock/doctype/delivery_stop/delivery_stop.py
@@ -1,9 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, newmatik.io / ESO Electronic Service Ottenbreit and contributors
# 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 f76bb87..c749b2e 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -1,8 +1,6 @@
-# -*- 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 datetime
@@ -10,8 +8,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):
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..321f48b 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -1,19 +1,23 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 ERPNextTestCase, create_test_contact_and_address
-class TestDeliveryTrip(unittest.TestCase):
+
+class TestDeliveryTrip(ERPNextTestCase):
def setUp(self):
+ super().setUp()
driver = create_driver()
create_vehicle()
create_delivery_notification()
@@ -27,6 +31,7 @@
frappe.db.sql("delete from `tabVehicle`")
frappe.db.sql("delete from `tabEmail Template`")
frappe.db.sql("delete from `tabDelivery Trip`")
+ return super().tearDown()
def test_expense_claim_fields_are_fetched_properly(self):
expense_claim = make_expense_claim(self.delivery_trip.name)
diff --git a/erpnext/stock/doctype/item/__init__.py b/erpnext/stock/doctype/item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/item/__init__.py
+++ b/erpnext/stock/doctype/item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index c587dd5..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",
@@ -793,3 +792,48 @@
}
}
});
+
+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..4f4e691 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -17,7 +17,6 @@
"variant_of",
"item_name",
"item_group",
- "is_item_from_hub",
"stock_uom",
"column_break0",
"disabled",
@@ -89,6 +88,7 @@
"sales_details",
"sales_uom",
"is_sales_item",
+ "grant_commission",
"column_break3",
"max_discount",
"deferred_revenue",
@@ -134,12 +134,7 @@
"website_specifications",
"web_long_description",
"website_content",
- "total_projected_qty",
- "hub_publishing_sb",
- "publish_in_hub",
- "hub_category_to_publish",
- "hub_warehouse",
- "synced_with_hub"
+ "total_projected_qty"
],
"fields": [
{
@@ -203,13 +198,6 @@
"search_index": 1
},
{
- "default": "0",
- "fieldname": "is_item_from_hub",
- "fieldtype": "Check",
- "label": "Is Item from Hub",
- "read_only": 1
- },
- {
"fieldname": "stock_uom",
"fieldtype": "Link",
"ignore_user_permissions": 1,
@@ -238,6 +226,7 @@
{
"bold": 1,
"default": "1",
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "is_stock_item",
"fieldtype": "Check",
"label": "Maintain Stock",
@@ -246,6 +235,7 @@
},
{
"default": "1",
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
"label": "Include Item In Manufacturing"
@@ -282,6 +272,7 @@
"fieldname": "asset_category",
"fieldtype": "Link",
"label": "Asset Category",
+ "mandatory_depends_on": "is_fixed_asset",
"options": "Asset Category"
},
{
@@ -434,8 +425,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 +483,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 +501,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "attributes",
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "variants_section",
"fieldtype": "Section Break",
"label": "Variants"
@@ -540,6 +532,7 @@
"options": "Item Variant Attribute"
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "defaults",
"fieldtype": "Section Break",
"label": "Sales, Purchase, Accounting Defaults"
@@ -621,6 +614,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "supplier_details",
"fieldtype": "Section Break",
"label": "Supplier Details"
@@ -668,6 +662,7 @@
},
{
"collapsible": 1,
+ "default": "eval:!doc.is_fixed_asset",
"fieldname": "sales_details",
"fieldtype": "Section Break",
"label": "Sales Details",
@@ -761,6 +756,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "customer_details",
"fieldtype": "Section Break",
"label": "Customer Details"
@@ -791,6 +787,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "inspection_criteria",
"fieldtype": "Section Break",
"label": "Inspection Criteria",
@@ -861,6 +858,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "website_section",
"fieldtype": "Section Break",
"label": "Website",
@@ -986,42 +984,7 @@
"read_only": 1
},
{
- "collapsible": 1,
- "depends_on": "eval:(!doc.is_item_from_hub)",
- "fieldname": "hub_publishing_sb",
- "fieldtype": "Section Break",
- "label": "Hub Publishing Details"
- },
- {
- "default": "0",
- "description": "Publish Item to hub.erpnext.com",
- "fieldname": "publish_in_hub",
- "fieldtype": "Check",
- "label": "Publish in Hub"
- },
- {
- "fieldname": "hub_category_to_publish",
- "fieldtype": "Data",
- "label": "Hub Category to Publish",
- "read_only": 1
- },
- {
- "description": "Publish \"In Stock\" or \"Not in Stock\" on Hub based on stock available in this warehouse.",
- "fieldname": "hub_warehouse",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Hub Warehouse",
- "options": "Warehouse"
- },
- {
- "default": "0",
- "fieldname": "synced_with_hub",
- "fieldtype": "Check",
- "label": "Synced With Hub",
- "read_only": 1
- },
- {
- "depends_on": "eval:!doc.__islocal",
+ "depends_on": "eval:!doc.__islocal && !doc.is_fixed_asset",
"fieldname": "over_delivery_receipt_allowance",
"fieldtype": "Float",
"label": "Over Delivery/Receipt Allowance (%)",
@@ -1029,7 +992,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 (%)"
@@ -1058,6 +1021,12 @@
"fieldname": "website_image_alt",
"fieldtype": "Data",
"label": "Image Description"
+ },
+ {
+ "default": "1",
+ "fieldname": "grant_commission",
+ "fieldtype": "Check",
+ "label": "Grant Commission"
}
],
"has_web_view": 1,
@@ -1066,11 +1035,11 @@
"image_field": "image",
"index_web_pages_for_search": 1,
"links": [],
- "max_attachments": 1,
- "modified": "2021-07-13 01:29:06.071827",
+ "modified": "2021-12-03 08:32:03.869294",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -1134,7 +1103,7 @@
"search_fields": "item_name,description,item_group,customer_code",
"show_name_in_global_search": 1,
"show_preview_popup": 1,
- "sort_field": "idx desc,modified desc",
+ "sort_field": "modified",
"sort_order": "DESC",
"title_field": "item_name",
"track_changes": 1
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 422fe3e..decf522 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
@@ -103,7 +123,6 @@
self.validate_barcode()
self.validate_warehouse_for_reorder()
self.update_bom_item_desc()
- self.synced_with_hub = 0
self.validate_has_variants()
self.validate_attributes_in_variants()
@@ -116,9 +135,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()
@@ -133,7 +152,6 @@
def on_update(self):
invalidate_cache_for_item(self)
- self.validate_name_with_item_group()
self.update_variants()
self.update_item_price()
self.update_template_item()
@@ -161,6 +179,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
})
@@ -202,10 +222,11 @@
'route')) + '/' + self.scrub((self.item_name or self.item_code) + '-' + random_string(5))
def validate_website_image(self):
+ """Validate if the website image is a public file"""
+
if frappe.flags.in_import:
return
- """Validate if the website image is a public file"""
auto_set_website_image = False
if not self.website_image and self.image:
auto_set_website_image = True
@@ -235,10 +256,11 @@
self.website_image = None
def make_thumbnail(self):
+ """Make a thumbnail of `website_image`"""
+
if frappe.flags.in_import:
return
- """Make a thumbnail of `website_image`"""
import requests.exceptions
if not self.is_new() and self.website_image != frappe.db.get_value(self.doctype, self.name, "website_image"):
@@ -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"""
@@ -603,16 +629,22 @@
where item_code = %s and is_cancelled = 0 limit 1""", self.name))
return self._stock_ledger_created
- def validate_name_with_item_group(self):
- # causes problem with tree build
- if frappe.db.exists("Item Group", self.name):
- frappe.throw(
- _("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()
@@ -639,6 +671,8 @@
def after_rename(self, old_name, new_name, merge):
if merge:
self.validate_duplicate_item_in_stock_reconciliation(old_name, new_name)
+ frappe.msgprint(_("It can take upto few hours for accurate stock values to be visible after merging items."),
+ indicator="orange", title="Note")
if self.route:
invalidate_cache_for_item(self)
@@ -690,7 +724,6 @@
def recalculate_bin_qty(self, new_name):
from erpnext.stock.stock_balance import repost_stock
- frappe.db.auto_commit_on_many_writes = 1
existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@@ -704,7 +737,6 @@
repost_stock(new_name, warehouse)
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
- frappe.db.auto_commit_on_many_writes = 0
@frappe.whitelist()
def copy_specification_from_item_group(self):
@@ -764,35 +796,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 \
@@ -1310,3 +1346,25 @@
@erpnext.allow_regional
def set_item_tax_from_hsn_code(item):
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..e16f5bb 100644
--- a/erpnext/stock/doctype/item/item_dashboard.py
+++ b/erpnext/stock/doctype/item/item_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'heatmap': True,
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..4028d93 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.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 unittest
-import frappe
+
import json
+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
-
+from erpnext.tests.utils import ERPNextTestCase, change_settings
test_ignore = ["BOM"]
test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
@@ -43,8 +51,9 @@
return item
-class TestItem(unittest.TestCase):
+class TestItem(ERPNextTestCase):
def setUp(self):
+ super().setUp()
frappe.flags.attribute_values = None
def get_item(self, idx):
@@ -222,6 +231,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)
@@ -462,7 +488,7 @@
item_doc.save()
# Check values saved correctly
- barcodes = frappe.get_list(
+ barcodes = frappe.get_all(
'Item Barcode',
fields=['barcode', 'barcode_type'],
filters={'parent': item_code})
@@ -508,8 +534,6 @@
def test_index_creation(self):
"check if index is getting created in db"
- from erpnext.stock.doctype.item.item import on_doctype_update
- on_doctype_update()
indices = frappe.db.sql("show index from tabItem", as_dict=1)
expected_columns = {"item_code", "item_name", "item_group", "route"}
diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py
index 190cb62..766647b 100644
--- a/erpnext/stock/doctype/item_alternative/item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/item_alternative.py
@@ -1,12 +1,12 @@
-# -*- 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
+
class ItemAlternative(Document):
def validate(self):
self.has_alternative_item()
@@ -23,19 +23,29 @@
frappe.throw(_("Alternative item must not be same as item code"))
item_meta = frappe.get_meta("Item")
- fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no","has_batch_no"]
- item_data = frappe.db.get_values("Item", self.item_code, fields, as_dict=1)
- alternative_item_data = frappe.db.get_values("Item", self.alternative_item_code, fields, as_dict=1)
+ fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no", "has_batch_no", "allow_alternative_item"]
+ item_data = frappe.db.get_value("Item", self.item_code, fields, as_dict=1)
+ alternative_item_data = frappe.db.get_value("Item", self.alternative_item_code, fields, as_dict=1)
for field in fields:
- if item_data[0].get(field) != alternative_item_data[0].get(field):
+ if item_data.get(field) != alternative_item_data.get(field):
raise_exception, alert = [1, False] if field == "is_stock_item" else [0, True]
frappe.msgprint(_("The value of {0} differs between Items {1} and {2}") \
.format(frappe.bold(item_meta.get_label(field)),
frappe.bold(self.alternative_item_code),
frappe.bold(self.item_code)),
- alert=alert, raise_exception=raise_exception)
+ alert=alert, raise_exception=raise_exception, indicator="Orange")
+
+ alternate_item_check_msg = _("Allow Alternative Item must be checked on Item {}")
+
+ if not item_data.allow_alternative_item:
+ frappe.throw(alternate_item_check_msg.format(self.item_code))
+ if self.two_way and not alternative_item_data.allow_alternative_item:
+ frappe.throw(alternate_item_check_msg.format(self.item_code))
+
+
+
def validate_duplicate(self):
if frappe.db.get_value("Item Alternative", {'item_code': self.item_code,
diff --git a/erpnext/stock/doctype/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..3976af4 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -1,20 +1,29 @@
-# -*- coding: utf-8 -*-
# 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 unittest
-class TestItemAlternative(unittest.TestCase):
+import json
+
+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,
+)
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestItemAlternative(ERPNextTestCase):
def setUp(self):
+ super().setUp()
make_items()
def test_alternative_item_for_subcontract_rm(self):
diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py
index 3764738..5a28a9e 100644
--- a/erpnext/stock/doctype/item_attribute/item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/item_attribute.py
@@ -1,14 +1,17 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
-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 07af176..0b7ca25 100644
--- a/erpnext/stock/doctype/item_attribute/test_item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
@@ -1,16 +1,18 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import frappe
-import unittest
test_records = frappe.get_test_records('Item Attribute')
from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
+from erpnext.tests.utils import ERPNextTestCase
-class TestItemAttribute(unittest.TestCase):
+
+class TestItemAttribute(ERPNextTestCase):
def setUp(self):
+ super().setUp()
if frappe.db.exists("Item Attribute", "_Test_Length"):
frappe.delete_doc("Item Attribute", "_Test_Length")
diff --git a/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py b/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py
index edbab00..bc6fb4f 100644
--- a/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py
+++ b/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ItemAttributeValue(Document):
pass
diff --git a/erpnext/stock/doctype/item_barcode/item_barcode.py b/erpnext/stock/doctype/item_barcode/item_barcode.py
index e85f93b..64c39da 100644
--- a/erpnext/stock/doctype/item_barcode/item_barcode.py
+++ b/erpnext/stock/doctype/item_barcode/item_barcode.py
@@ -1,8 +1,6 @@
-# -*- 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
diff --git a/erpnext/stock/doctype/item_customer_detail/__init__.py b/erpnext/stock/doctype/item_customer_detail/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/item_customer_detail/__init__.py
+++ b/erpnext/stock/doctype/item_customer_detail/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 3e4e850..ba81b44 100644
--- a/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py
+++ b/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class ItemCustomerDetail(Document):
pass
diff --git a/erpnext/stock/doctype/item_default/item_default.py b/erpnext/stock/doctype/item_default/item_default.py
index 935f0ff..8a9693e 100644
--- a/erpnext/stock/doctype/item_default/item_default.py
+++ b/erpnext/stock/doctype/item_default/item_default.py
@@ -1,9 +1,9 @@
-# -*- 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 ItemDefault(Document):
pass
diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
index 939abf8..469ccd8 100644
--- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
+++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
@@ -1,13 +1,12 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 ItemManufacturer(Document):
def validate(self):
self.validate_duplicate_entry()
diff --git a/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py
index 1cef20c..de04356 100644
--- a/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py
+++ b/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# import frappe
import unittest
+
class TestItemManufacturer(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/item_price/__init__.py b/erpnext/stock/doctype/item_price/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/item_price/__init__.py
+++ b/erpnext/stock/doctype/item_price/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index e82a19b..010e01a 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -1,10 +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
from frappe import _
from frappe.model.document import Document
+from frappe.utils import getdate
class ItemPriceDuplicateItem(frappe.ValidationError):
@@ -26,7 +27,7 @@
def validate_dates(self):
if self.valid_from and self.valid_upto:
- if self.valid_from > self.valid_upto:
+ if getdate(self.valid_from) > getdate(self.valid_upto):
frappe.throw(_("Valid From Date must be lesser than Valid Upto Date."))
def update_price_list_details(self):
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index f3d406e..f81770e 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -1,16 +1,18 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-import unittest
+
import frappe
from frappe.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
+from erpnext.tests.utils import ERPNextTestCase
-class TestItemPrice(unittest.TestCase):
+class TestItemPrice(ERPNextTestCase):
def setUp(self):
+ super().setUp()
frappe.db.sql("delete from `tabItem Price`")
make_test_records_for_doctype("Item Price", force=True)
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/__init__.py b/erpnext/stock/doctype/item_quality_inspection_parameter/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/__init__.py
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 785737b..6cadb99 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class ItemQualityInspectionParameter(Document):
pass
diff --git a/erpnext/stock/doctype/item_reorder/item_reorder.py b/erpnext/stock/doctype/item_reorder/item_reorder.py
index 5cdaa22..c3cc69b 100644
--- a/erpnext/stock/doctype/item_reorder/item_reorder.py
+++ b/erpnext/stock/doctype/item_reorder/item_reorder.py
@@ -3,10 +3,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
diff --git a/erpnext/stock/doctype/item_supplier/__init__.py b/erpnext/stock/doctype/item_supplier/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/item_supplier/__init__.py
+++ b/erpnext/stock/doctype/item_supplier/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/item_supplier/item_supplier.py b/erpnext/stock/doctype/item_supplier/item_supplier.py
index 5dda535..84f5556 100644
--- a/erpnext/stock/doctype/item_supplier/item_supplier.py
+++ b/erpnext/stock/doctype/item_supplier/item_supplier.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class ItemSupplier(Document):
pass
diff --git a/erpnext/stock/doctype/item_tax/__init__.py b/erpnext/stock/doctype/item_tax/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/item_tax/__init__.py
+++ b/erpnext/stock/doctype/item_tax/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/item_tax/item_tax.py b/erpnext/stock/doctype/item_tax/item_tax.py
index 7c9e811..aa82719 100644
--- a/erpnext/stock/doctype/item_tax/item_tax.py
+++ b/erpnext/stock/doctype/item_tax/item_tax.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class ItemTax(Document):
pass
diff --git a/erpnext/stock/doctype/item_variant/item_variant.py b/erpnext/stock/doctype/item_variant/item_variant.py
index 5d5a022..f1580fc 100644
--- a/erpnext/stock/doctype/item_variant/item_variant.py
+++ b/erpnext/stock/doctype/item_variant/item_variant.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..76b88b8 100644
--- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py
+++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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..f63498b 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
@@ -1,11 +1,11 @@
-# -*- 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.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..5f33d67 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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 e3041cf..af9612c 100644
--- a/erpnext/stock/doctype/item_website_specification/item_website_specification.py
+++ b/erpnext/stock/doctype/item_website_specification/item_website_specification.py
@@ -3,10 +3,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
diff --git a/erpnext/stock/doctype/landed_cost_item/__init__.py b/erpnext/stock/doctype/landed_cost_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/landed_cost_item/__init__.py
+++ b/erpnext/stock/doctype/landed_cost_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 493e8b2..35a3740 100644
--- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py
+++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class LandedCostItem(Document):
pass
diff --git a/erpnext/stock/doctype/landed_cost_purchase_receipt/__init__.py b/erpnext/stock/doctype/landed_cost_purchase_receipt/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/landed_cost_purchase_receipt/__init__.py
+++ b/erpnext/stock/doctype/landed_cost_purchase_receipt/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 38f4eaf..f5bbc4a 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
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class LandedCostPurchaseReceipt(Document):
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 0dc396a..a4a1c58 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
@@ -1,9 +1,9 @@
# 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.model.document import Document
+
class LandedCostTaxesandCharges(Document):
pass
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
index 433f78a..9c1a809 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
@@ -35,7 +35,7 @@
refresh() {
var help_content =
`<br><br>
- <table class="table table-bordered" style="background-color: #f9f9f9;">
+ <table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
<tr><td>
<h4>
<i class="fa fa-hand-right"></i>
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..7aff95d 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -1,15 +1,17 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..9204842 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -2,18 +2,21 @@
# License: GNU General Public License v3. See license.txt
-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.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
-class TestLandedCostVoucher(unittest.TestCase):
+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.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,
+)
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestLandedCostVoucher(ERPNextTestCase):
def test_landed_cost_voucher(self):
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
@@ -208,7 +211,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..426affc 100644
--- a/erpnext/stock/doctype/manufacturer/manufacturer.py
+++ b/erpnext/stock/doctype/manufacturer/manufacturer.py
@@ -1,12 +1,11 @@
-# -*- 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.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..6632347 100644
--- a/erpnext/stock/doctype/manufacturer/test_manufacturer.py
+++ b/erpnext/stock/doctype/manufacturer/test_manufacturer.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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.py b/erpnext/stock/doctype/material_request/material_request.py
index 026b85e..103e8d6 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -4,20 +4,19 @@
# ERPNext - web based ERP (http://erpnext.com)
# 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 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"
@@ -81,6 +80,9 @@
# NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated
# Though the creation of Material Request from a Production Plan can be rethought to fix this
+ self.reset_default_field_value("set_warehouse", "items", "warehouse")
+ self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
+
def set_title(self):
'''Set title as comma separated list of items'''
if not self.title:
@@ -271,7 +273,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, str):
+ args = json.loads(args)
def postprocess(source, target_doc):
if frappe.flags.args and frappe.flags.args.default_supplier:
@@ -286,9 +292,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 +324,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": {
@@ -494,7 +503,8 @@
"field_map": {
"name": "material_request_item",
"parent": "material_request",
- "uom": "stock_uom"
+ "uom": "stock_uom",
+ "job_card_item": "job_card_item"
},
"postprocess": update_item,
"condition": lambda doc: doc.ordered_qty < doc.stock_qty
diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py
index e1e4faf..c1ce0a9 100644
--- a/erpnext/stock/doctype/material_request/material_request_dashboard.py
+++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
from frappe import _
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..383b0ae 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -4,14 +4,21 @@
# ERPNext - web based ERP (http://erpnext.com)
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe, unittest, erpnext
-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
-class TestMaterialRequest(unittest.TestCase):
+import frappe
+from frappe.utils import flt, today
+
+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,
+)
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestMaterialRequest(ERPNextTestCase):
def test_make_purchase_order(self):
mr = frappe.copy_doc(test_records[0]).insert()
diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json
index 25bbbbd..2bad42a 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.json
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.json
@@ -52,6 +52,7 @@
"sales_order_item",
"production_plan",
"material_request_plan_item",
+ "job_card_item",
"col_break4",
"expense_account",
"section_break_46",
@@ -444,16 +445,25 @@
{
"fieldname": "qty_info_col_break",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "job_card_item",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "no_copy": 1,
+ "print_hide": 1,
+ "label": "Job Card Item"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-10-02 11:44:36.553064",
+ "modified": "2021-11-03 14:40:24.409826",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request Item",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
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 e0066e6..32407d0 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.py
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.py
@@ -3,11 +3,11 @@
# 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
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..e4091c4 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -3,11 +3,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 +43,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 +92,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 = []
@@ -99,9 +108,67 @@
packed_items = doc.get("packed_items")
doc.set("packed_items", [])
+
for d in packed_items:
if d not in delete_list:
- doc.append("packed_items", d)
+ add_item_to_packing_list(doc, d)
+
+def add_item_to_packing_list(doc, packed_item):
+ doc.append("packed_items", {
+ 'parent_item': packed_item.parent_item,
+ 'item_code': packed_item.item_code,
+ 'item_name': packed_item.item_name,
+ 'uom': packed_item.uom,
+ 'qty': packed_item.qty,
+ 'rate': packed_item.rate,
+ 'conversion_factor': packed_item.conversion_factor,
+ 'description': packed_item.description,
+ 'warehouse': packed_item.warehouse,
+ 'batch_no': packed_item.batch_no,
+ 'actual_batch_qty': packed_item.actual_batch_qty,
+ 'serial_no': packed_item.serial_no,
+ 'target_warehouse': packed_item.target_warehouse,
+ 'actual_qty': packed_item.actual_qty,
+ 'projected_qty': packed_item.projected_qty,
+ 'incoming_rate': packed_item.incoming_rate,
+ 'prevdoc_doctype': packed_item.prevdoc_doctype,
+ 'parent_detail_docname': packed_item.parent_detail_docname
+ })
+
+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_item_rate = bundle_item.rate if bundle_item.rate else 0
+ bundle_price = bundle_item.qty * bundle_item_rate
+ 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):
diff --git a/erpnext/stock/doctype/packing_slip/__init__.py b/erpnext/stock/doctype/packing_slip/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/packing_slip/__init__.py
+++ b/erpnext/stock/doctype/packing_slip/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.py b/erpnext/stock/doctype/packing_slip/packing_slip.py
index 4a843e0..b092862 100644
--- a/erpnext/stock/doctype/packing_slip/packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/packing_slip.py
@@ -1,7 +1,6 @@
# 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 import _
diff --git a/erpnext/stock/doctype/packing_slip/test_packing_slip.py b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
index 1f2af02..5eb6b73 100644
--- a/erpnext/stock/doctype/packing_slip/test_packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
@@ -1,12 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Packing Slip')
+from erpnext.tests.utils import ERPNextTestCase
+
class TestPackingSlip(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/packing_slip_item/__init__.py b/erpnext/stock/doctype/packing_slip_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/packing_slip_item/__init__.py
+++ b/erpnext/stock/doctype/packing_slip_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 b0a8559..ec148aa 100644
--- a/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py
+++ b/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py
@@ -3,10 +3,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
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index 2146793..c604c71 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -18,7 +18,9 @@
"get_item_locations",
"section_break_6",
"locations",
- "amended_from"
+ "amended_from",
+ "print_settings_section",
+ "group_same_items"
],
"fields": [
{
@@ -110,14 +112,28 @@
"options": "STO-PICK-.YYYY.-",
"reqd": 1,
"set_only_once": 1
+ },
+ {
+ "fieldname": "print_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Print Settings"
+ },
+ {
+ "allow_on_submit": 1,
+ "default": "0",
+ "fieldname": "group_same_items",
+ "fieldtype": "Check",
+ "label": "Group Same Items",
+ "print_hide": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-03-17 11:38:41.932875",
+ "modified": "2021-10-05 15:08:40.369957",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -184,4 +200,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 516ae43..5484a11 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -1,18 +1,19 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# 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 collections import OrderedDict, defaultdict
+
+import frappe
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
+from frappe.model.document import Document
+from frappe.model.mapper import map_child_doc
+from frappe.utils import cint, floor, flt, today
+
+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
@@ -116,6 +117,34 @@
and (self.for_qty is None or self.for_qty == 0):
frappe.throw(_("Qty of Finished Goods Item should be greater than 0."))
+ def before_print(self, settings=None):
+ if self.get("group_same_items"):
+ self.group_similar_items()
+
+ def group_similar_items(self):
+ group_item_qty = defaultdict(float)
+ group_picked_qty = defaultdict(float)
+
+ for item in self.locations:
+ group_item_qty[(item.item_code, item.warehouse)] += item.qty
+ group_picked_qty[(item.item_code, item.warehouse)] += item.picked_qty
+
+ duplicate_list = []
+ for item in self.locations:
+ if (item.item_code, item.warehouse) in group_item_qty:
+ item.qty = group_item_qty[(item.item_code, item.warehouse)]
+ item.picked_qty = group_picked_qty[(item.item_code, item.warehouse)]
+ item.stock_qty = group_item_qty[(item.item_code, item.warehouse)]
+ del group_item_qty[(item.item_code, item.warehouse)]
+ else:
+ duplicate_list.append(item)
+
+ for item in duplicate_list:
+ self.remove(item)
+
+ for idx, item in enumerate(self.locations, start=1):
+ item.idx = idx
+
def validate_item_locations(pick_list):
if not pick_list.locations:
@@ -216,7 +245,7 @@
warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no)
locations = []
- for warehouse, serial_nos in iteritems(warehouse_serial_nos_map):
+ for warehouse, serial_nos in warehouse_serial_nos_map.items():
locations.append({
'qty': len(serial_nos),
'warehouse': warehouse,
diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
index 7c321c4..ec3047e 100644
--- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
+++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
'fieldname': 'pick_list',
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index 84566b8..41e3150 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -1,19 +1,21 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
import frappe
-import unittest
+from frappe import _dict
+
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,
+)
+from erpnext.tests.utils import ERPNextTestCase
-class TestPickList(unittest.TestCase):
+
+class TestPickList(ERPNextTestCase):
def test_pick_list_picks_warehouse_for_each_item(self):
try:
@@ -353,6 +355,39 @@
sales_order.cancel()
purchase_receipt.cancel()
+ def test_pick_list_grouping_before_print(self):
+ def _compare_dicts(a, b):
+ "compare dicts but ignore missing keys in `a`"
+ for key, value in a.items():
+ self.assertEqual(b.get(key), value, msg=f"{key} doesn't match")
+
+ # nothing should be grouped
+ pl = frappe.get_doc(doctype="Pick List", group_same_items=True, locations=[
+ _dict(item_code="A", warehouse="X", qty=1, picked_qty=2),
+ _dict(item_code="B", warehouse="X", qty=1, picked_qty=2),
+ _dict(item_code="A", warehouse="Y", qty=1, picked_qty=2),
+ _dict(item_code="B", warehouse="Y", qty=1, picked_qty=2),
+ ])
+ pl.before_print()
+ self.assertEqual(len(pl.locations), 4)
+
+ # grouping should halve the number of items
+ pl = frappe.get_doc(doctype="Pick List", group_same_items=True, locations=[
+ _dict(item_code="A", warehouse="X", qty=5, picked_qty=1),
+ _dict(item_code="B", warehouse="Y", qty=4, picked_qty=2),
+ _dict(item_code="A", warehouse="X", qty=3, picked_qty=2),
+ _dict(item_code="B", warehouse="Y", qty=2, picked_qty=2),
+ ])
+ pl.before_print()
+ self.assertEqual(len(pl.locations), 2)
+
+ expected_items = [
+ _dict(item_code="A", warehouse="X", qty=8, picked_qty=3),
+ _dict(item_code="B", warehouse="Y", qty=6, picked_qty=4),
+ ]
+ for expected_item, created_item in zip(expected_items, pl.locations):
+ _compare_dicts(expected_item, created_item)
+
# def test_pick_list_skips_items_in_expired_batch(self):
# pass
diff --git a/erpnext/stock/doctype/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..6ecccb1 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.py
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 PickListItem(Document):
pass
diff --git a/erpnext/stock/doctype/price_list/__init__.py b/erpnext/stock/doctype/price_list/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/price_list/__init__.py
+++ b/erpnext/stock/doctype/price_list/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py
index 002d3d8..74b823a 100644
--- a/erpnext/stock/doctype/price_list/price_list.py
+++ b/erpnext/stock/doctype/price_list/price_list.py
@@ -1,12 +1,12 @@
# 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 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):
@@ -37,7 +37,9 @@
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
+ 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
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 2c287c9..b8218b9 100644
--- a/erpnext/stock/doctype/price_list/test_price_list.py
+++ b/erpnext/stock/doctype/price_list/test_price_list.py
@@ -1,7 +1,7 @@
# 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
# test_ignore = ["Item"]
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..94e1107 100644
--- a/erpnext/stock/doctype/price_list_country/price_list_country.py
+++ b/erpnext/stock/doctype/price_list_country/price_list_country.py
@@ -1,10 +1,9 @@
-# -*- 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.model.document import Document
+
class PriceListCountry(Document):
pass
diff --git a/erpnext/stock/doctype/purchase_receipt/__init__.py b/erpnext/stock/doctype/purchase_receipt/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/purchase_receipt/__init__.py
+++ b/erpnext/stock/doctype/purchase_receipt/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 1a59734..112dded 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -1140,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
@@ -1149,7 +1150,7 @@
"idx": 261,
"is_submittable": 1,
"links": [],
- "modified": "2021-08-17 20:16:40.849885",
+ "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..c97b306 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -1,22 +1,18 @@
# 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 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 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 = {
@@ -122,6 +118,10 @@
if getdate(self.posting_date) > getdate(nowdate()):
throw(_("Posting Date cannot be future date"))
+ self.reset_default_field_value("set_warehouse", "items", "warehouse")
+ self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
+ self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
+
def validate_cwip_accounts(self):
for item in self.get('items'):
@@ -254,7 +254,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)
@@ -359,7 +361,7 @@
# Amount added through landed-cos-voucher
if d.landed_cost_voucher_amount and landed_cost_entries:
- for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]):
+ for account, amount in landed_cost_entries[(d.item_code, d.name)].items():
account_currency = get_account_currency(account)
credit_amount = (flt(amount["base_amount"]) if (amount["base_amount"] or
account_currency!=self.company_currency) else flt(amount["amount"]))
@@ -842,7 +844,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..bdc5435 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
from frappe import _
+
def get_data():
return {
'fieldname': 'purchase_receipt_no',
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 2314508..2909a2d 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1,40 +1,76 @@
# 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 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
-from six import iteritems
-from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
-from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-class TestPurchaseReceipt(unittest.TestCase):
+import json
+import unittest
+
+import frappe
+from frappe.utils import add_days, cint, cstr, flt, today
+
+import erpnext
+from erpnext.accounts.doctype.account.test_account import get_inventory_account
+from erpnext.controllers.buying_controller import QtyMismatchError
+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.stock_ledger import SerialNoExistsInFutureTransaction
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestPurchaseReceipt(ERPNextTestCase):
def setUp(self):
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
+ def test_purchase_receipt_received_qty(self):
+ """
+ 1. Test if received qty is validated against accepted + rejected
+ 2. Test if received qty is auto set on save
+ """
+ pr = make_purchase_receipt(
+ qty=1,
+ rejected_qty=1,
+ received_qty=3,
+ item_code="_Test Item Home Desktop 200",
+ do_not_save=True
+ )
+ self.assertRaises(QtyMismatchError, pr.save)
+
+ pr.items[0].received_qty = 0
+ pr.save()
+ self.assertEqual(pr.items[0].received_qty, 2)
+
+ # teardown
+ pr.delete()
+
def test_reverse_purchase_receipt_sle(self):
pr = make_purchase_receipt(qty=0.5, item_code="_Test Item Home Desktop 200")
- sl_entry = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
- "voucher_no": pr.name}, ['actual_qty'])
+ sl_entry = frappe.db.get_all(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Purchase Receipt",
+ "voucher_no": pr.name
+ },
+ ['actual_qty']
+ )
self.assertEqual(len(sl_entry), 1)
self.assertEqual(sl_entry[0].actual_qty, 0.5)
pr.cancel()
- sl_entry_cancelled = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
- "voucher_no": pr.name}, ['actual_qty'], order_by='creation')
+ sl_entry_cancelled = frappe.db.get_all(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Purchase Receipt",
+ "voucher_no": pr.name
+ },
+ ['actual_qty'],
+ order_by='creation'
+ )
self.assertEqual(len(sl_entry_cancelled), 2)
self.assertEqual(sl_entry_cancelled[1].actual_qty, -0.5)
@@ -60,8 +96,15 @@
}]
}).insert()
- template = frappe.db.get_value('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice')
- old_template_in_supplier = frappe.db.get_value("Supplier", "_Test Supplier", "payment_terms")
+ template = frappe.db.get_value(
+ "Payment Terms Template",
+ "_Test Payment Terms Template For Purchase Invoice"
+ )
+ old_template_in_supplier = frappe.db.get_value(
+ "Supplier",
+ "_Test Supplier",
+ "payment_terms"
+ )
frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", template)
pr = make_purchase_receipt(do_not_save=True)
@@ -87,30 +130,59 @@
# teardown
pi.delete() # draft PI
pr.cancel()
- frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", old_template_in_supplier)
- frappe.get_doc('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice').delete()
+ frappe.db.set_value(
+ "Supplier",
+ "_Test Supplier",
+ "payment_terms",
+ old_template_in_supplier
+ )
+ frappe.get_doc(
+ "Payment Terms Template",
+ "_Test Payment Terms Template For Purchase Invoice"
+ ).delete()
def test_purchase_receipt_no_gl_entry(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
- company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
-
- existing_bin_qty, existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
- "warehouse": "_Test Warehouse - _TC"}, ["actual_qty", "stock_value"])
+ existing_bin_qty, existing_bin_stock_value = frappe.db.get_value(
+ "Bin",
+ {
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC"
+ },
+ ["actual_qty", "stock_value"]
+ )
if existing_bin_qty < 0:
- make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=abs(existing_bin_qty))
+ make_stock_entry(
+ item_code="_Test Item",
+ target="_Test Warehouse - _TC",
+ qty=abs(existing_bin_qty)
+ )
pr = make_purchase_receipt()
- stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
- {"voucher_type": "Purchase Receipt", "voucher_no": pr.name,
- "item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, "stock_value_difference")
+ stock_value_difference = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Purchase Receipt",
+ "voucher_no": pr.name,
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC"
+ },
+ "stock_value_difference"
+ )
self.assertEqual(stock_value_difference, 250)
- current_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
- "warehouse": "_Test Warehouse - _TC"}, "stock_value")
+ current_bin_stock_value = frappe.db.get_value(
+ "Bin",
+ {
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC"
+ },
+ "stock_value"
+ )
self.assertEqual(current_bin_stock_value, existing_bin_stock_value + 250)
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
@@ -132,13 +204,17 @@
pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500)
- self.assertTrue(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
+ self.assertTrue(
+ frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})
+ )
pr.load_from_db()
batch_no = pr.items[0].batch_no
pr.cancel()
- self.assertFalse(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
+ self.assertFalse(
+ frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})
+ )
self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
def test_duplicate_serial_nos(self):
@@ -157,42 +233,78 @@
pr = make_purchase_receipt(item_code=item.name, qty=2, rate=500)
pr.load_from_db()
- serial_nos = frappe.db.get_value('Stock Ledger Entry',
- {'voucher_type': 'Purchase Receipt', 'voucher_no': pr.name, 'item_code': item.name}, 'serial_no')
+ serial_nos = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Purchase Receipt",
+ "voucher_no": pr.name,
+ "item_code": item.name
+ },
+ "serial_no"
+ )
serial_nos = get_serial_nos(serial_nos)
self.assertEquals(get_serial_nos(pr.items[0].serial_no), serial_nos)
# Then tried to receive same serial nos in difference company
- pr_different_company = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
- serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
- warehouse = 'Stores - _TC1')
+ pr_different_company = make_purchase_receipt(
+ item_code=item.name,
+ qty=2,
+ rate=500,
+ serial_no='\n'.join(serial_nos),
+ company='_Test Company 1',
+ do_not_submit=True,
+ warehouse = 'Stores - _TC1'
+ )
self.assertRaises(SerialNoDuplicateError, pr_different_company.submit)
# Then made delivery note to remove the serial nos from stock
- dn = create_delivery_note(item_code=item.name, qty=2, rate = 1500, serial_no='\n'.join(serial_nos))
+ dn = create_delivery_note(
+ item_code=item.name,
+ qty=2,
+ rate=1500,
+ serial_no='\n'.join(serial_nos)
+ )
dn.load_from_db()
self.assertEquals(get_serial_nos(dn.items[0].serial_no), serial_nos)
posting_date = add_days(today(), -3)
# Try to receive same serial nos again in the same company with backdated.
- pr1 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
- posting_date=posting_date, serial_no='\n'.join(serial_nos), do_not_submit=True)
+ pr1 = make_purchase_receipt(
+ item_code=item.name,
+ qty=2,
+ rate=500,
+ posting_date=posting_date,
+ serial_no='\n'.join(serial_nos),
+ do_not_submit=True
+ )
self.assertRaises(SerialNoExistsInFutureTransaction, pr1.submit)
# Try to receive same serial nos with different company with backdated.
- pr2 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
- posting_date=posting_date, serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
- warehouse = 'Stores - _TC1')
+ pr2 = make_purchase_receipt(
+ item_code=item.name,
+ qty=2,
+ rate=500,
+ posting_date=posting_date,
+ serial_no='\n'.join(serial_nos),
+ company="_Test Company 1",
+ do_not_submit=True,
+ warehouse="Stores - _TC1"
+ )
self.assertRaises(SerialNoExistsInFutureTransaction, pr2.submit)
# Receive the same serial nos after the delivery note posting date and time
- make_purchase_receipt(item_code=item.name, qty=2, rate=500, serial_no='\n'.join(serial_nos))
+ make_purchase_receipt(
+ item_code=item.name,
+ qty=2,
+ rate=500,
+ serial_no='\n'.join(serial_nos)
+ )
# Raise the error for backdated deliver note entry cancel
self.assertRaises(SerialNoExistsInFutureTransaction, dn.cancel)
@@ -235,11 +347,23 @@
def test_subcontracting(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
- frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM")
- make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100)
- make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
- qty=100, basic_rate=100)
- pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes")
+ frappe.db.set_value(
+ "Buying Settings", None,
+ "backflush_raw_materials_of_subcontract_based_on", "BOM"
+ )
+
+ make_stock_entry(
+ item_code="_Test Item", qty=100,
+ target="_Test Warehouse 1 - _TC", basic_rate=100
+ )
+ make_stock_entry(
+ item_code="_Test Item Home Desktop 100", qty=100,
+ target="_Test Warehouse 1 - _TC", basic_rate=100
+ )
+ pr = make_purchase_receipt(
+ item_code="_Test FG Item", qty=10,
+ rate=500, is_subcontracted="Yes"
+ )
self.assertEqual(len(pr.get("supplied_items")), 2)
rm_supp_cost = sum(d.amount for d in pr.get("supplied_items"))
@@ -249,17 +373,33 @@
def test_subcontracting_gle_fg_item_rate_zero(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
- frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM")
+ frappe.db.set_value(
+ "Buying Settings", None,
+ "backflush_raw_materials_of_subcontract_based_on", "BOM"
+ )
- se1 = make_stock_entry(item_code="_Test Item", target="Work In Progress - TCP1",
- qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
+ se1 = make_stock_entry(
+ item_code="_Test Item",
+ target="Work In Progress - TCP1",
+ qty=100, basic_rate=100,
+ company="_Test Company with perpetual inventory"
+ )
- se2 = make_stock_entry(item_code="_Test Item Home Desktop 100", target="Work In Progress - TCP1",
- qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
+ se2 = make_stock_entry(
+ item_code="_Test Item Home Desktop 100",
+ target="Work In Progress - TCP1",
+ qty=100, basic_rate=100,
+ company="_Test Company with perpetual inventory"
+ )
- pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=0, is_subcontracted="Yes",
- company="_Test Company with perpetual inventory", warehouse='Stores - TCP1',
- supplier_warehouse='Work In Progress - TCP1')
+ pr = make_purchase_receipt(
+ item_code="_Test FG Item",
+ qty=10, rate=0,
+ is_subcontracted="Yes",
+ company="_Test Company with perpetual inventory",
+ warehouse="Stores - TCP1",
+ supplier_warehouse="Work In Progress - TCP1"
+ )
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
@@ -275,11 +415,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"
@@ -288,13 +433,23 @@
po = create_purchase_order(item_code=item_code, qty=1, include_exploded_items=0,
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
- #stock raw materials in a warehouse before transfer
- se1 = make_stock_entry(target="_Test Warehouse - _TC",
- item_code = "Test Extra Item 1", qty=10, basic_rate=100)
- se2 = make_stock_entry(target="_Test Warehouse - _TC",
- item_code = "_Test FG Item", qty=1, basic_rate=100)
- se3 = make_stock_entry(target="_Test Warehouse - _TC",
- item_code = "Test Extra Item 2", qty=1, basic_rate=100)
+ # stock raw materials in a warehouse before transfer
+ make_stock_entry(
+ target="_Test Warehouse - _TC",
+ item_code = "Test Extra Item 1",
+ qty=10, basic_rate=100
+ )
+ make_stock_entry(
+ target="_Test Warehouse - _TC",
+ item_code = "_Test FG Item",
+ qty=1, basic_rate=100
+ )
+ make_stock_entry(
+ target="_Test Warehouse - _TC",
+ item_code = "Test Extra Item 2",
+ qty=1, basic_rate=100
+ )
+
rm_items = [
{
"item_code": item_code,
@@ -328,11 +483,17 @@
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"),
- pr.supplier)
+ pr_row_1_serial_no = pr.get("items")[0].serial_no
+
+ self.assertEqual(
+ frappe.db.get_value("Serial No", pr_row_1_serial_no, "supplier"),
+ pr.supplier
+ )
pr.cancel()
- self.assertFalse(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "warehouse"))
+ self.assertFalse(
+ frappe.db.get_value("Serial No", pr_row_1_serial_no, "warehouse")
+ )
def test_rejected_serial_no(self):
pr = frappe.copy_doc(test_records[0])
@@ -359,18 +520,33 @@
pr.cancel()
def test_purchase_return_partial(self):
- pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
- warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
+ pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1"
+ )
- return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
- warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
- is_return=1, return_against=pr.name, qty=-2, do_not_submit=1)
+ return_pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1",
+ is_return=1,
+ return_against=pr.name,
+ qty=-2,
+ do_not_submit=1
+ )
return_pr.items[0].purchase_receipt_item = pr.items[0].name
return_pr.submit()
# check sle
- outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
- "voucher_no": return_pr.name}, "outgoing_rate")
+ outgoing_rate = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Purchase Receipt",
+ "voucher_no": return_pr.name
+ },
+ "outgoing_rate"
+ )
self.assertEqual(outgoing_rate, 50)
@@ -434,11 +610,21 @@
pr.cancel()
def test_purchase_return_full(self):
- pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
- supplier_warehouse = "Work in Progress - TCP1")
+ pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1"
+ )
- return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
- supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-5, do_not_submit=1)
+ return_pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1",
+ is_return=1,
+ return_against=pr.name,
+ qty=-5,
+ do_not_submit=1
+ )
return_pr.items[0].purchase_receipt_item = pr.items[0].name
return_pr.submit()
@@ -460,15 +646,41 @@
rejected_warehouse="_Test Rejected Warehouse - TCP1"
if not frappe.db.exists("Warehouse", rejected_warehouse):
- get_warehouse(company = "_Test Company with perpetual inventory",
- abbr = " - TCP1", warehouse_name = "_Test Rejected Warehouse").name
+ get_warehouse(
+ company = "_Test Company with perpetual inventory",
+ abbr = " - TCP1",
+ warehouse_name = "_Test Rejected Warehouse"
+ ).name
- pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", received_qty=4, qty=2, rejected_warehouse=rejected_warehouse)
+ pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1",
+ qty=2,
+ rejected_qty=2,
+ rejected_warehouse=rejected_warehouse
+ )
- return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, received_qty = -4, qty=-2, rejected_warehouse=rejected_warehouse)
+ return_pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1",
+ is_return=1,
+ return_against=pr.name,
+ qty=-2,
+ rejected_qty = -2,
+ rejected_warehouse=rejected_warehouse
+ )
- actual_qty = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
- "voucher_no": return_pr.name, 'warehouse': return_pr.items[0].rejected_warehouse}, "actual_qty")
+ actual_qty = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {
+ "voucher_type": "Purchase Receipt",
+ "voucher_no": return_pr.name,
+ "warehouse": return_pr.items[0].rejected_warehouse
+ },
+ "actual_qty"
+ )
self.assertEqual(actual_qty, -2)
@@ -479,7 +691,7 @@
def test_purchase_return_for_serialized_items(self):
def _check_serial_no_values(serial_no, field_values):
serial_no = frappe.get_doc("Serial No", serial_no)
- for field, value in iteritems(field_values):
+ for field, value in field_values.items():
self.assertEqual(cstr(serial_no.get(field)), value)
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -493,8 +705,13 @@
"purchase_document_no": pr.name
})
- return_pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=-1,
- is_return=1, return_against=pr.name, serial_no=serial_no)
+ return_pr = make_purchase_receipt(
+ item_code="_Test Serialized Item With Series",
+ qty=-1,
+ is_return=1,
+ return_against=pr.name,
+ serial_no=serial_no
+ )
_check_serial_no_values(serial_no, {
"warehouse": "",
@@ -516,9 +733,21 @@
})
row.db_update()
- pr = make_purchase_receipt(item_code=item_code, qty=1, uom="Box", conversion_factor=1.0)
- return_pr = make_purchase_receipt(item_code=item_code, qty=-10, uom="Unit",
- stock_uom="Box", conversion_factor=0.1, is_return=1, return_against=pr.name)
+ pr = make_purchase_receipt(
+ item_code=item_code,
+ qty=1,
+ uom="Box",
+ conversion_factor=1.0
+ )
+ return_pr = make_purchase_receipt(
+ item_code=item_code,
+ qty=-10,
+ uom="Unit",
+ stock_uom="Box",
+ conversion_factor=0.1,
+ is_return=1,
+ return_against=pr.name
+ )
self.assertEqual(abs(return_pr.items[0].stock_qty), 1.0)
@@ -526,22 +755,32 @@
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()
update_purchase_receipt_status(pr.name, "Closed")
- self.assertEqual(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed")
+ self.assertEqual(
+ frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed"
+ )
pr.reload()
pr.cancel()
def test_pr_billing_status(self):
- # PO -> PR1 -> PI and PO -> PI and PO -> PR2
+ """Flow:
+ 1. PO -> PR1 -> PI
+ 2. PO -> PI
+ 3. PO -> PR2.
+ """
+ from erpnext.buying.doctype.purchase_order.purchase_order import (
+ make_purchase_invoice as make_purchase_invoice_from_po,
+ )
+ 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()
@@ -600,21 +839,39 @@
pr_doc = make_purchase_receipt(item_code=item_code,
qty=1, serial_no = serial_no)
- self.assertEqual(serial_no, frappe.db.get_value("Serial No",
- {"purchase_document_type": "Purchase Receipt", "purchase_document_no": pr_doc.name}, "name"))
+ self.assertEqual(
+ serial_no,
+ frappe.db.get_value(
+ "Serial No",
+ {
+ "purchase_document_type": "Purchase Receipt",
+ "purchase_document_no": pr_doc.name
+ },
+ "name"
+ )
+ )
pr_doc.cancel()
- #check for the auto created serial nos
+ # check for the auto created serial nos
item_code = "Test Auto Created Serial No"
if not frappe.db.exists("Item", item_code):
- item = make_item(item_code, dict(has_serial_no=1, serial_no_series="KLJL.###"))
+ make_item(item_code, dict(has_serial_no=1, serial_no_series="KLJL.###"))
new_pr_doc = make_purchase_receipt(item_code=item_code, qty=1)
serial_no = get_serial_nos(new_pr_doc.items[0].serial_no)[0]
- self.assertEqual(serial_no, frappe.db.get_value("Serial No",
- {"purchase_document_type": "Purchase Receipt", "purchase_document_no": new_pr_doc.name}, "name"))
+ self.assertEqual(
+ serial_no,
+ frappe.db.get_value(
+ "Serial No",
+ {
+ "purchase_document_type": "Purchase Receipt",
+ "purchase_document_no": new_pr_doc.name
+ },
+ "name"
+ )
+ )
new_pr_doc.cancel()
@@ -690,8 +947,12 @@
def test_purchase_receipt_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
cost_center = "_Test Cost Center for BS Account - TCP1"
- create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company with perpetual inventory")
+ create_cost_center(
+ cost_center_name="_Test Cost Center for BS Account",
+ company="_Test Company with perpetual inventory"
+ )
if not frappe.db.exists('Location', 'Test Location'):
frappe.get_doc({
@@ -699,10 +960,16 @@
'location_name': 'Test Location'
}).insert()
- pr = make_purchase_receipt(cost_center=cost_center, company="_Test Company with perpetual inventory",
- warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
+ pr = make_purchase_receipt(
+ cost_center=cost_center,
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1"
+ )
- stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
+ stock_in_hand_account = get_inventory_account(
+ pr.company, pr.get("items")[0].warehouse
+ )
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
self.assertTrue(gl_entries)
@@ -726,9 +993,16 @@
'doctype': 'Location',
'location_name': 'Test Location'
}).insert()
- pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
- stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
+ pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ supplier_warehouse = "Work in Progress - TCP1"
+ )
+
+ stock_in_hand_account = get_inventory_account(
+ pr.company, pr.get("items")[0].warehouse
+ )
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
self.assertTrue(gl_entries)
@@ -748,12 +1022,19 @@
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)
- pr1 = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-1, do_not_submit=True)
+ pr1 = make_purchase_receipt(
+ qty=-1,
+ is_return=1, return_against=pr.name,
+ do_not_submit=True
+ )
pr1.items[0].purchase_order = po.name
pr1.items[0].purchase_order_item = po.items[0].name
pr1.items[0].purchase_receipt_item = pr.items[0].name
@@ -786,7 +1067,11 @@
pi1.save()
pi1.submit()
- pr2 = make_purchase_receipt(is_return=1, return_against=pr1.name, qty=-2, do_not_submit=True)
+ pr2 = make_purchase_receipt(
+ qty=-2,
+ is_return=1, return_against=pr1.name,
+ do_not_submit=True
+ )
pr2.items[0].purchase_receipt_item = pr1.items[0].name
pr2.submit()
@@ -828,14 +1113,22 @@
pr1.cancel()
def test_stock_transfer_from_purchase_receipt_with_valuation(self):
- create_warehouse("_Test Warehouse for Valuation", company="_Test Company with perpetual inventory",
- properties={"account": '_Test Account Stock In Hand - TCP1'})
+ create_warehouse(
+ "_Test Warehouse for Valuation",
+ company="_Test Company with perpetual inventory",
+ properties={"account": '_Test Account Stock In Hand - TCP1'}
+ )
- pr1 = make_purchase_receipt(warehouse = '_Test Warehouse for Valuation - TCP1',
- company="_Test Company with perpetual inventory")
+ pr1 = make_purchase_receipt(
+ warehouse = '_Test Warehouse for Valuation - TCP1',
+ company="_Test Company with perpetual inventory"
+ )
- pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
- warehouse = "Stores - TCP1", do_not_save=1)
+ pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1",
+ do_not_save=1
+ )
pr.items[0].from_warehouse = '_Test Warehouse for Valuation - TCP1'
pr.supplier_warehouse = ''
@@ -879,10 +1172,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"
@@ -912,10 +1210,24 @@
}
rm_items = [
- {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 3","item_name":"_Test Item",
- "qty":300,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[0].name},
- {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 3","item_name":"_Test Item",
- "qty":200,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[0].name}
+ {
+ "item_code":item_code,
+ "rm_item_code":"Sub Contracted Raw Material 3",
+ "item_name":"_Test Item",
+ "qty":300,
+ "warehouse":"_Test Warehouse - _TC",
+ "stock_uom":"Nos",
+ "name": po.supplied_items[0].name
+ },
+ {
+ "item_code":item_code,
+ "rm_item_code":"Sub Contracted Raw Material 3",
+ "item_name":"_Test Item",
+ "qty":200,
+ "warehouse":"_Test Warehouse - _TC",
+ "stock_uom":"Nos",
+ "name": po.supplied_items[0].name
+ }
]
rm_item_string = json.dumps(rm_items)
@@ -925,8 +1237,14 @@
se.items[1].batch_no = ste2.items[0].batch_no
se.submit()
- supplied_qty = frappe.db.get_value("Purchase Order Item Supplied",
- {"parent": po.name, "rm_item_code": "Sub Contracted Raw Material 3"}, "supplied_qty")
+ supplied_qty = frappe.db.get_value(
+ "Purchase Order Item Supplied",
+ {
+ "parent": po.name,
+ "rm_item_code": "Sub Contracted Raw Material 3"
+ },
+ "supplied_qty"
+ )
self.assertEqual(supplied_qty, 500.00)
@@ -952,8 +1270,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()
@@ -974,8 +1291,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()
@@ -1000,10 +1316,18 @@
company = '_Test Company with perpetual inventory'
service_item = '_Test Non Stock Item'
- before_test_value = frappe.db.get_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items')
- frappe.db.set_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items', 1)
+ before_test_value = frappe.db.get_value(
+ 'Company', company, 'enable_perpetual_inventory_for_non_stock_items'
+ )
+ frappe.db.set_value(
+ 'Company', company,
+ 'enable_perpetual_inventory_for_non_stock_items', 1
+ )
srbnb_account = 'Stock Received But Not Billed - TCP1'
- frappe.db.set_value('Company', company, 'service_received_but_not_billed', srbnb_account)
+ frappe.db.set_value(
+ 'Company', company,
+ 'service_received_but_not_billed', srbnb_account
+ )
pr = make_purchase_receipt(
company=company, item=service_item,
@@ -1035,11 +1359,18 @@
self.assertEqual(len(item_one_gl_entry), 1)
self.assertEqual(len(item_two_gl_entry), 1)
- frappe.db.set_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items', before_test_value)
+ 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",
@@ -1056,19 +1387,36 @@
pr.submit()
# Get exchnage gain and loss account
- exchange_gain_loss_account = frappe.db.get_value('Company', pr.company, 'exchange_gain_loss_account')
+ exchange_gain_loss_account = frappe.db.get_value(
+ 'Company', pr.company, 'exchange_gain_loss_account'
+ )
# fetching the latest GL Entry with exchange gain and loss account account
- amount = frappe.db.get_value('GL Entry', {'account': exchange_gain_loss_account, 'voucher_no': pr.name}, 'credit')
+ amount = frappe.db.get_value(
+ 'GL Entry',
+ {
+ 'account': exchange_gain_loss_account,
+ 'voucher_no': pr.name
+ },
+ 'credit'
+ )
discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount)
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()
@@ -1197,8 +1545,8 @@
pr.return_against = args.return_against
pr.apply_putaway_rule = args.apply_putaway_rule
qty = args.qty or 5
- received_qty = args.received_qty or qty
- rejected_qty = args.rejected_qty or flt(received_qty) - flt(qty)
+ rejected_qty = args.rejected_qty or 0
+ received_qty = args.received_qty or flt(rejected_qty) + flt(qty)
item_code = args.item or args.item_code or "_Test Item"
uom = args.uom or frappe.db.get_value("Item", item_code, "stock_uom") or "_Test UOM"
@@ -1221,9 +1569,12 @@
if args.get_multiple_items:
pr.items = []
- for item in get_items(warehouse= args.warehouse, cost_center = args.cost_center or frappe.get_cached_value('Company', pr.company, 'cost_center')):
- pr.append("items", item)
+ company_cost_center = frappe.get_cached_value('Company', pr.company, 'cost_center')
+ cost_center = args.cost_center or company_cost_center
+
+ for item in get_items(warehouse=args.warehouse, cost_center=cost_center):
+ pr.append("items", item)
if args.get_taxes_and_charges:
for tax in get_taxes():
diff --git a/erpnext/stock/doctype/purchase_receipt_item/__init__.py b/erpnext/stock/doctype/purchase_receipt_item/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/__init__.py
+++ b/erpnext/stock/doctype/purchase_receipt_item/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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..30ea1c3 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",
@@ -196,6 +197,7 @@
},
{
"bold": 1,
+ "default": "0",
"fieldname": "received_qty",
"fieldtype": "Float",
"label": "Received Quantity",
@@ -203,6 +205,7 @@
"oldfieldtype": "Currency",
"print_hide": 1,
"print_width": "100px",
+ "read_only": 1,
"reqd": 1,
"width": "100px"
},
@@ -218,8 +221,10 @@
"width": "100px"
},
{
+ "columns": 1,
"fieldname": "rejected_qty",
"fieldtype": "Float",
+ "in_list_view": 1,
"label": "Rejected Quantity",
"oldfieldname": "rejected_qty",
"oldfieldtype": "Currency",
@@ -326,7 +331,7 @@
},
{
"bold": 1,
- "columns": 3,
+ "columns": 2,
"fieldname": "rate",
"fieldtype": "Currency",
"in_list_view": 1,
@@ -542,6 +547,7 @@
"fieldname": "stock_qty",
"fieldtype": "Float",
"label": "Accepted Qty in Stock UOM",
+ "no_copy": 1,
"oldfieldname": "stock_qty",
"oldfieldtype": "Currency",
"print_hide": 1,
@@ -881,7 +887,9 @@
"fieldname": "received_stock_qty",
"fieldtype": "Float",
"label": "Received Qty in Stock UOM",
- "print_hide": 1
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
},
{
"depends_on": "eval: doc.uom != doc.stock_uom",
@@ -956,15 +964,23 @@
"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-11-15 15:46:10.591600",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
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..b4b9fd3 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
@@ -1,10 +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 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 315e723..523ba12 100644
--- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -1,18 +1,19 @@
-# -*- 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 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 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):
@@ -73,7 +74,7 @@
purpose: Purpose of Stock Entry
sync (optional): Sync with client side only for client side calls
"""
- if isinstance(items, string_types):
+ if isinstance(items, str):
items = json.loads(items)
items_not_accomodated, updated_table = [], []
diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
index 0590ae1..bd4d811 100644
--- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -1,17 +1,18 @@
-# -*- 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 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
-from erpnext.stock.doctype.batch.test_batch import make_new_batch
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
-class TestPutawayRule(unittest.TestCase):
+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
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestPutawayRule(ERPNextTestCase):
def setUp(self):
if not frappe.db.exists("Item", "_Rice"):
make_item("_Rice", {
diff --git a/erpnext/stock/doctype/quality_inspection/__init__.py b/erpnext/stock/doctype/quality_inspection/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/quality_inspection/__init__.py
+++ b/erpnext/stock/doctype/quality_inspection/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index 469511a..913ee15 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -1,14 +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 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/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index f5d076a..308c628 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -1,8 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-import unittest
-
import frappe
from frappe.utils import nowdate
@@ -15,12 +13,14 @@
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.tests.utils import ERPNextTestCase
# test_records = frappe.get_test_records('Quality Inspection')
-class TestQualityInspection(unittest.TestCase):
+class TestQualityInspection(ERPNextTestCase):
def setUp(self):
+ super().setUp()
create_item("_Test Item with QA")
frappe.db.set_value(
"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py
index 8678422..d5123c7 100644
--- a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py
+++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..3cc1fde 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
@@ -1,10 +1,9 @@
-# -*- 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 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..26e9361 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
@@ -1,10 +1,10 @@
-# -*- 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 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..1630ad0 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
@@ -1,10 +1,9 @@
-# -*- 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 TestQualityInspectionParameterGroup(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/quality_inspection_reading/__init__.py b/erpnext/stock/doctype/quality_inspection_reading/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/__init__.py
+++ b/erpnext/stock/doctype/quality_inspection_reading/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 b10fa31..81454f1 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py
+++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class QualityInspectionReading(Document):
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 971b3c2..7f8c871 100644
--- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
+++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
@@ -1,11 +1,11 @@
-# -*- 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 QualityInspectionTemplate(Document):
pass
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..9523bba 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
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..7a0f5d0 100644
--- a/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py
+++ b/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
index a800bf8..cd7e63b 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
@@ -64,7 +64,7 @@
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
- "options": "Queued\nIn Progress\nCompleted\nFailed",
+ "options": "Queued\nIn Progress\nCompleted\nSkipped\nFailed",
"read_only": 1
},
{
@@ -177,10 +177,11 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-07-22 18:59:43.057878",
+ "modified": "2021-11-24 02:18:10.524560",
"modified_by": "Administrator",
"module": "Stock",
"name": "Repost Item Valuation",
+ "naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -206,27 +207,12 @@
"print": 1,
"read": 1,
"report": 1,
- "role": "Stock User",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
"role": "Stock Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
- "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -234,7 +220,7 @@
"print": 1,
"read": 1,
"report": 1,
- "role": "Accounts User",
+ "role": "Accounts Manager",
"share": 1,
"submit": 1,
"write": 1
@@ -242,4 +228,4 @@
],
"sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
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..b2ad07f 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -1,19 +1,24 @@
-# -*- 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, 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()
+ self.set_status(write=False)
self.reset_field_values()
self.set_company()
@@ -21,23 +26,27 @@
if self.based_on == 'Transaction':
self.item_code = None
self.warehouse = None
- else:
- self.voucher_type = None
- self.voucher_no = None
+
+ self.allow_negative_stock = self.allow_negative_stock or \
+ cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
def set_company(self):
- if self.voucher_type and self.voucher_no:
+ if self.based_on == "Transaction":
self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
elif self.warehouse:
self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company")
- def set_status(self, status=None):
+ def set_status(self, status=None, write=True):
+ status = status or self.status
if not status:
- status = 'Queued'
- self.db_set('status', status)
+ self.status = 'Queued'
+ else:
+ self.status = status
+ if write:
+ self.db_set('status', self.status)
def on_submit(self):
- if not frappe.flags.in_test:
+ if not frappe.flags.in_test or self.flags.dont_run_in_test:
return
frappe.enqueue(repost, timeout=1800, queue='long',
@@ -45,9 +54,42 @@
@frappe.whitelist()
def restart_reposting(self):
- self.set_status('Queued')
- frappe.enqueue(repost, timeout=1800, queue='long',
- job_name='repost_sle', now=True, doc=self)
+ self.set_status('Queued', write=False)
+ self.current_index = 0
+ self.distinct_item_and_warehouse = None
+ self.items_to_be_repost = None
+ self.db_update()
+
+ def deduplicate_similar_repost(self):
+ """ Deduplicate similar reposts based on item-warehouse-posting combination."""
+ if self.based_on != "Item and Warehouse":
+ return
+
+ filters = {
+ "item_code": self.item_code,
+ "warehouse": self.warehouse,
+ "name": self.name,
+ "posting_date": self.posting_date,
+ "posting_time": self.posting_time,
+ }
+
+ frappe.db.sql("""
+ update `tabRepost Item Valuation`
+ set status = 'Skipped'
+ WHERE item_code = %(item_code)s
+ and warehouse = %(warehouse)s
+ and name != %(name)s
+ and TIMESTAMP(posting_date, posting_time) > TIMESTAMP(%(posting_date)s, %(posting_time)s)
+ and docstatus = 1
+ and status = 'Queued'
+ and based_on = 'Item and Warehouse'
+ """,
+ filters
+ )
+
+def on_doctype_update():
+ frappe.db.add_index("Repost Item Valuation", ["warehouse", "item_code"], "item_warehouse")
+
def repost(doc):
try:
@@ -118,11 +160,16 @@
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:
- doc = frappe.get_cached_doc('Repost Item Valuation', row.name)
- repost(doc)
+ doc = frappe.get_doc('Repost Item Valuation', row.name)
+ if doc.status in ('Queued', 'In Progress'):
+ repost(doc)
+ doc.deduplicate_similar_repost()
riv_entries = get_repost_item_valuation_entries()
if riv_entries:
@@ -136,3 +183,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..78b432d 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,164 @@
-# -*- 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 frappe.utils import nowdate
+
+from erpnext.controllers.stock_controller import create_item_wise_repost_entries
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
+ in_configured_timeslot,
+)
+from erpnext.stock.utils import PendingRepostingError
+
+
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}",
+ )
+
+ def test_create_item_wise_repost_item_valuation_entries(self):
+ pr = make_purchase_receipt(
+ company="_Test Company with perpetual inventory",
+ warehouse="Stores - TCP1",
+ get_multiple_items=True,
+ )
+
+ rivs = create_item_wise_repost_entries(pr.doctype, pr.name)
+ self.assertGreaterEqual(len(rivs), 2)
+ self.assertIn("_Test Item", [d.item_code for d in rivs])
+
+ for riv in rivs:
+ self.assertEqual(riv.company, "_Test Company with perpetual inventory")
+ self.assertEqual(riv.warehouse, "Stores - TCP1")
+
+ def test_deduplication(self):
+ def _assert_status(doc, status):
+ doc.load_from_db()
+ self.assertEqual(doc.status, status)
+
+ riv_args = frappe._dict(
+ doctype="Repost Item Valuation",
+ item_code="_Test Item",
+ warehouse="_Test Warehouse - _TC",
+ based_on="Item and Warehouse",
+ voucher_type="Sales Invoice",
+ voucher_no="SI-1",
+ posting_date="2021-01-02",
+ posting_time="00:01:00",
+ )
+
+ # new repost without any duplicates
+ riv1 = frappe.get_doc(riv_args)
+ riv1.flags.dont_run_in_test = True
+ riv1.submit()
+ _assert_status(riv1, "Queued")
+ self.assertEqual(riv1.voucher_type, "Sales Invoice") # traceability
+ self.assertEqual(riv1.voucher_no, "SI-1")
+
+ # newer than existing duplicate - riv1
+ riv2 = frappe.get_doc(riv_args.update({"posting_date": "2021-01-03"}))
+ riv2.flags.dont_run_in_test = True
+ riv2.submit()
+ riv1.deduplicate_similar_repost()
+ _assert_status(riv2, "Skipped")
+
+ # older than exisitng duplicate - riv1
+ riv3 = frappe.get_doc(riv_args.update({"posting_date": "2021-01-01"}))
+ riv3.flags.dont_run_in_test = True
+ riv3.submit()
+ riv3.deduplicate_similar_repost()
+ _assert_status(riv3, "Queued")
+ _assert_status(riv1, "Skipped")
+
+ # unrelated reposts, shouldn't do anything to others.
+ riv4 = frappe.get_doc(riv_args.update({"warehouse": "Stores - _TC"}))
+ riv4.flags.dont_run_in_test = True
+ riv4.submit()
+ riv4.deduplicate_similar_repost()
+ _assert_status(riv4, "Queued")
+ _assert_status(riv3, "Queued")
+
+ # to avoid breaking other tests accidentaly
+ riv4.set_status("Skipped")
+ riv3.set_status("Skipped")
+
+ def test_stock_freeze_validation(self):
+
+ today = nowdate()
+
+ riv = frappe.get_doc(
+ doctype="Repost Item Valuation",
+ item_code="_Test Item",
+ warehouse="_Test Warehouse - _TC",
+ based_on="Item and Warehouse",
+ posting_date=today,
+ posting_time="00:01:00",
+ )
+ riv.flags.dont_run_in_test = True # keep it queued
+ riv.submit()
+
+ stock_settings = frappe.get_doc("Stock Settings")
+ stock_settings.stock_frozen_upto = today
+
+ self.assertRaises(PendingRepostingError, stock_settings.save)
+
+ riv.set_status("Skipped")
diff --git a/erpnext/stock/doctype/serial_no/__init__.py b/erpnext/stock/doctype/serial_no/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/serial_no/__init__.py
+++ b/erpnext/stock/doctype/serial_no/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 70312bc..38291d1 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -1,19 +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 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 frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate
from erpnext.controllers.stock_controller import StockController
-from six import string_types
-from six.moves import map
+from erpnext.stock.get_item_details import get_reserved_qty_for_so
+
class SerialNoCannotCreateDirectError(ValidationError): pass
class SerialNoCannotCannotChangeError(ValidationError): pass
@@ -344,7 +342,7 @@
is_stock_reco = sle.voucher_type == "Stock Reconciliation"
msg = None
- if sr and (actual_qty < 0 or is_stock_reco) and sr.warehouse != sle.warehouse:
+ if sr and (actual_qty < 0 or is_stock_reco) and (sr.warehouse and sr.warehouse != sle.warehouse):
# receipt(inward) is being cancelled
msg = _("Cannot cancel {0} {1} as Serial No {2} does not belong to the warehouse {3}").format(
sle.voucher_type, doc_link, sr_link, frappe.bold(sle.warehouse))
@@ -573,7 +571,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:
@@ -590,7 +588,7 @@
@frappe.whitelist()
def get_pos_reserved_serial_nos(filters):
- if isinstance(filters, string_types):
+ if isinstance(filters, str):
filters = json.loads(filters)
pos_transacted_sr_nos = frappe.db.sql("""select item.serial_no as serial_no
@@ -610,7 +608,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 0eccce3..99000d1 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -4,21 +4,23 @@
# ERPNext - web based ERP (http://erpnext.com)
# 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 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"]
test_records = frappe.get_test_records('Serial No')
from erpnext.stock.doctype.serial_no.serial_no import *
+from erpnext.tests.utils import ERPNextTestCase
-class TestSerialNo(unittest.TestCase):
+
+class TestSerialNo(ERPNextTestCase):
def test_cannot_create_direct(self):
frappe.delete_doc_if_exists("Serial No", "_TCSER0001")
@@ -181,14 +183,14 @@
se = frappe.copy_doc(test_records[0])
se.get("items")[0].item_code = item_code
- se.get("items")[0].qty = 3
- se.get("items")[0].serial_no = " _TS1, _TS2 , _TS3 "
- se.get("items")[0].transfer_qty = 3
+ se.get("items")[0].qty = 4
+ se.get("items")[0].serial_no = " _TS1, _TS2 , _TS3 , _TS4 - 2021"
+ se.get("items")[0].transfer_qty = 4
se.set_stock_entry_type()
se.insert()
se.submit()
- self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3")
+ self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3\n_TS4 - 2021")
frappe.db.rollback()
diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py
index 01fcee4..666de57 100644
--- a/erpnext/stock/doctype/shipment/shipment.py
+++ b/erpnext/stock/doctype/shipment/shipment.py
@@ -1,14 +1,15 @@
-# -*- 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.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/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index db2f116..705b265 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
from datetime import date, timedelta
import frappe
-import unittest
-from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
-class TestShipment(unittest.TestCase):
+from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestShipment(ERPNextTestCase):
def test_shipment_from_delivery_note(self):
delivery_note = create_test_delivery_note()
delivery_note.submit()
@@ -44,7 +45,6 @@
}
)
delivery_note.insert()
- frappe.db.commit()
return delivery_note
@@ -88,7 +88,6 @@
}
)
shipment.insert()
- frappe.db.commit()
return shipment
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..2b58a39 100644
--- a/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py
+++ b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..a607021 100644
--- a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py
+++ b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..a5de312 100644
--- a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py
+++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..b6b7ca6 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
@@ -1,10 +1,9 @@
-# -*- 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 TestShipmentParcelTemplate(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/stock_entry/__init__.py b/erpnext/stock/doctype/stock_entry/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/stock_entry/__init__.py
+++ b/erpnext/stock/doctype/stock_entry/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 8f34794..c4b8131 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -88,7 +88,11 @@
}
}
- filters["warehouse"] = item.s_warehouse || item.t_warehouse;
+ // User could want to select a manually created empty batch (no warehouse)
+ // or a pre-existing batch
+ if (frm.doc.purpose != "Material Receipt") {
+ filters["warehouse"] = item.s_warehouse || item.t_warehouse;
+ }
return {
query : "erpnext.controllers.queries.get_batch_no",
@@ -278,7 +282,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 +327,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 +558,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 +754,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,5 +1068,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.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 0b4592c..a00d63e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1,28 +1,39 @@
# 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, 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
+
+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
@@ -92,6 +103,8 @@
self.set_actual_qty()
self.calculate_rate_and_amount()
self.validate_putaway_capacity()
+ self.reset_default_field_value("from_warehouse", "items", "s_warehouse")
+ self.reset_default_field_value("to_warehouse", "items", "t_warehouse")
def on_submit(self):
self.update_stock_ledger()
@@ -272,10 +285,10 @@
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):
+ for item_code, qty_list in item_wise_qty.items():
total = flt(sum(qty_list), frappe.get_precision("Stock Entry Detail", "qty"))
if self.fg_completed_qty != total:
frappe.throw(_("The finished product {0} quantity {1} and For Quantity {2} cannot be different")
@@ -333,7 +346,7 @@
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))
@@ -465,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'):
@@ -486,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):
@@ -532,7 +547,7 @@
scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])
# Get raw materials cost from BOM if multiple material consumption entries
- if frappe.db.get_single_value("Manufacturing Settings", "material_consumption", cache=True):
+ if not outgoing_items_cost and frappe.db.get_single_value("Manufacturing Settings", "material_consumption", cache=True):
bom_items = self.get_bom_raw_materials(finished_item_qty)
outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
@@ -540,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"):
@@ -670,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)
@@ -790,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:
@@ -801,24 +825,28 @@
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"):
- for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})):
+ for account, amount in item_account_wise_additional_cost.get((d.item_code, d.name), {}).items():
if not amount: continue
gl_entries.append(self.get_gl_dict({
@@ -990,7 +1018,7 @@
if self.work_order and self.purpose == "Material Transfer for Manufacture":
item_dict = self.get_pending_raw_materials(backflush_based_on)
if self.to_warehouse and self.pro_doc:
- for item in itervalues(item_dict):
+ for item in item_dict.values():
item["to_warehouse"] = self.pro_doc.wip_warehouse
self.add_to_stock_entry_detail(item_dict)
@@ -1021,7 +1049,7 @@
WHERE
po.name = poitemsup.parent and po.name = %s """,self.purchase_order))
- for item in itervalues(item_dict):
+ for item in item_dict.values():
if self.pro_doc and cint(self.pro_doc.from_wip_warehouse):
item["from_warehouse"] = self.pro_doc.wip_warehouse
#Get Reserve Warehouse from PO
@@ -1043,13 +1071,14 @@
self.set_scrap_items()
self.set_actual_qty()
+ self.update_items_for_process_loss()
self.validate_customer_provided_item()
self.calculate_rate_and_amount()
def set_scrap_items(self):
if self.purpose != "Send to Subcontractor" and self.purpose in ["Manufacture", "Repack"]:
scrap_item_dict = self.get_bom_scrap_material(self.fg_completed_qty)
- for item in itervalues(scrap_item_dict):
+ for item in scrap_item_dict.values():
item.idx = ''
if self.pro_doc and self.pro_doc.scrap_warehouse:
item["to_warehouse"] = self.pro_doc.scrap_warehouse
@@ -1153,7 +1182,7 @@
fetch_exploded = self.use_multi_level_bom, fetch_qty_in_stock_uom=False)
used_alternative_items = get_used_alternative_items(work_order = self.work_order)
- for item in itervalues(item_dict):
+ for item in item_dict.values():
# if source warehouse presents in BOM set from_warehouse as bom source_warehouse
if item["allow_alternative_item"]:
item["allow_alternative_item"] = frappe.db.get_value('Work Order',
@@ -1176,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):
+ for item in item_dict.values():
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',
@@ -1197,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)
@@ -1249,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
@@ -1334,7 +1438,7 @@
if transfer_limit_qty >= to_transfer_qty:
allow_overproduction = True
- for item, item_details in iteritems(item_dict):
+ for item, item_details in item_dict.items():
pending_to_issue = flt(item_details.required_qty) - flt(item_details.transferred_qty)
desire_to_transfer = flt(self.fg_completed_qty) * flt(item_details.required_qty) / max_qty
@@ -1348,7 +1452,7 @@
item_dict[item]["qty"] = 0
# delete items with 0 qty
- list_of_items = item_dict.keys()
+ list_of_items = list(item_dict.keys())
for item in list_of_items:
if not item_dict[item]["qty"]:
del item_dict[item]
@@ -1360,51 +1464,94 @@
return item_dict
def get_pro_order_required_items(self, backflush_based_on=None):
- item_dict = frappe._dict()
- pro_order = frappe.get_doc("Work Order", self.work_order)
- if not frappe.db.get_value("Warehouse", pro_order.wip_warehouse, "is_group"):
- wip_warehouse = pro_order.wip_warehouse
+ """
+ Gets Work Order Required Items only if Stock Entry purpose is **Material Transferred for Manufacture**.
+ """
+ item_dict, job_card_items = frappe._dict(), []
+ work_order = frappe.get_doc("Work Order", self.work_order)
+
+ consider_job_card = work_order.transfer_material_against == "Job Card" and self.get("job_card")
+ if consider_job_card:
+ job_card_items = self.get_job_card_item_codes(self.get("job_card"))
+
+ if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"):
+ wip_warehouse = work_order.wip_warehouse
else:
wip_warehouse = None
- for d in pro_order.get("required_items"):
- if ( ((flt(d.required_qty) > flt(d.transferred_qty)) or
- (backflush_based_on == "Material Transferred for Manufacture")) and
- (d.include_item_in_manufacturing or self.purpose != "Material Transfer for Manufacture")):
+ for d in work_order.get("required_items"):
+ if consider_job_card and (d.item_code not in job_card_items):
+ continue
+
+ transfer_pending = flt(d.required_qty) > flt(d.transferred_qty)
+ can_transfer = transfer_pending or (backflush_based_on == "Material Transferred for Manufacture")
+
+ if not can_transfer:
+ continue
+
+ if d.include_item_in_manufacturing:
item_row = d.as_dict()
+ item_row["idx"] = len(item_dict) + 1
+
+ if consider_job_card:
+ job_card_item = frappe.db.get_value(
+ "Job Card Item",
+ {
+ "item_code": d.item_code,
+ "parent": self.get("job_card")
+ }
+ )
+ item_row["job_card_item"] = job_card_item or None
+
if d.source_warehouse and not frappe.db.get_value("Warehouse", d.source_warehouse, "is_group"):
item_row["from_warehouse"] = d.source_warehouse
item_row["to_warehouse"] = wip_warehouse
if item_row["allow_alternative_item"]:
- item_row["allow_alternative_item"] = pro_order.allow_alternative_item
+ item_row["allow_alternative_item"] = work_order.allow_alternative_item
item_dict.setdefault(d.item_code, item_row)
return item_dict
+ def get_job_card_item_codes(self, job_card=None):
+ if not job_card:
+ return []
+
+ job_card_items = frappe.get_all(
+ "Job Card Item",
+ filters={
+ "parent": job_card
+ },
+ fields=["item_code"],
+ distinct=True
+ )
+ return [d.item_code for d in job_card_items]
+
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
for d in item_dict:
- stock_uom = item_dict[d].get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
+ item_row = item_dict[d]
+ stock_uom = item_row.get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
se_child = self.append('items')
- se_child.s_warehouse = item_dict[d].get("from_warehouse")
- se_child.t_warehouse = item_dict[d].get("to_warehouse")
- se_child.item_code = item_dict[d].get('item_code') or cstr(d)
- se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom
+ se_child.s_warehouse = item_row.get("from_warehouse")
+ se_child.t_warehouse = item_row.get("to_warehouse")
+ se_child.item_code = item_row.get('item_code') or cstr(d)
+ se_child.uom = item_row["uom"] if item_row.get("uom") else stock_uom
se_child.stock_uom = stock_uom
- se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty"))
- se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
- se_child.subcontracted_item = item_dict[d].get("main_item_code")
- se_child.cost_center = (item_dict[d].get("cost_center") or
- 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.qty = flt(item_row["qty"], se_child.precision("qty"))
+ se_child.allow_alternative_item = item_row.get("allow_alternative_item", 0)
+ se_child.subcontracted_item = item_row.get("main_item_code")
+ se_child.cost_center = (item_row.get("cost_center") or
+ get_default_cost_center(item_row, company = self.company))
+ se_child.is_finished_item = item_row.get("is_finished_item", 0)
+ se_child.is_scrap_item = item_row.get("is_scrap_item", 0)
+ se_child.is_process_loss = item_row.get("is_process_loss", 0)
- for field in ["idx", "po_detail", "original_item",
- "expense_account", "description", "item_name", "serial_no", "batch_no"]:
- if item_dict[d].get(field):
- se_child.set(field, item_dict[d].get(field))
+ for field in ["idx", "po_detail", "original_item", "expense_account",
+ "description", "item_name", "serial_no", "batch_no", "allow_zero_valuation_rate"]:
+ if item_row.get(field):
+ se_child.set(field, item_row.get(field))
if se_child.s_warehouse==None:
se_child.s_warehouse = self.from_warehouse
@@ -1412,12 +1559,11 @@
se_child.t_warehouse = self.to_warehouse
# in stock uom
- se_child.conversion_factor = flt(item_dict[d].get("conversion_factor")) or 1
- se_child.transfer_qty = flt(item_dict[d]["qty"]*se_child.conversion_factor, se_child.precision("qty"))
+ se_child.conversion_factor = flt(item_row.get("conversion_factor")) or 1
+ se_child.transfer_qty = flt(item_row["qty"]*se_child.conversion_factor, se_child.precision("qty"))
-
- # to be assigned for finished item
- se_child.bom_no = bom_no
+ se_child.bom_no = bom_no # to be assigned for finished item
+ se_child.job_card_item = item_row.get("job_card_item") if self.get("job_card") else None
def validate_with_material_request(self):
for item in self.get("items"):
@@ -1487,7 +1633,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:
@@ -1579,6 +1726,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:
@@ -1619,7 +1789,7 @@
@frappe.whitelist()
def move_sample_to_retention_warehouse(company, items):
- if isinstance(items, string_types):
+ if isinstance(items, str):
items = json.loads(items)
retention_warehouse = frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse')
stock_entry = frappe.new_doc("Stock Entry")
@@ -1805,7 +1975,7 @@
@frappe.whitelist()
def get_warehouse_details(args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index 563fcb0..17266ad 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -1,11 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe.utils import cint, flt
-from six import string_types
+import erpnext
+
@frappe.whitelist()
def make_stock_entry(**args):
@@ -58,7 +59,7 @@
if args.apply_putaway_rule:
s.apply_putaway_rule = args.apply_putaway_rule
- if isinstance(args.qty, string_types):
+ if isinstance(args.qty, str):
if '.' in args.qty:
args.qty = flt(args.qty)
else:
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index a0e7051..5a9e77e 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1,25 +1,36 @@
# 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
-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 frappe.utils import flt, nowdate, nowtime
+
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 six import iteritems
+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 NegativeStockError, get_previous_sle
+from erpnext.tests.utils import ERPNextTestCase, change_settings
+
def get_sle(**args):
condition, values = "", []
- for key, value in iteritems(args):
+ for key, value in args.items():
condition += " and " if condition else " where "
condition += "`{0}`=%s".format(key)
values.append(value)
@@ -28,9 +39,10 @@
order by timestamp(posting_date, posting_time) desc, creation desc limit 1"""% condition,
values, as_dict=1)
-class TestStockEntry(unittest.TestCase):
+class TestStockEntry(ERPNextTestCase):
def tearDown(self):
frappe.set_user("Administrator")
+ frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "0")
def test_fifo(self):
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@@ -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"])
@@ -571,6 +584,65 @@
self.assertEqual(fg_cost,
flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2))
+ def test_work_order_manufacture_with_material_consumption(self):
+ from erpnext.manufacturing.doctype.work_order.work_order import (
+ make_stock_entry as _make_stock_entry,
+ )
+ frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "1")
+
+ bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item",
+ "is_default": 1, "docstatus": 1})
+
+ work_order = frappe.new_doc("Work Order")
+ work_order.update({
+ "company": "_Test Company",
+ "fg_warehouse": "_Test Warehouse 1 - _TC",
+ "production_item": "_Test FG Item",
+ "bom_no": bom_no,
+ "qty": 1.0,
+ "stock_uom": "_Test UOM",
+ "wip_warehouse": "_Test Warehouse - _TC"
+ })
+ work_order.insert()
+ work_order.submit()
+
+ make_stock_entry(item_code="_Test Item",
+ target="Stores - _TC", qty=10, basic_rate=5000.0)
+ make_stock_entry(item_code="_Test Item Home Desktop 100",
+ target="Stores - _TC", qty=10, basic_rate=1000.0)
+
+
+ s = frappe.get_doc(_make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1))
+ for d in s.get("items"):
+ d.s_warehouse = "Stores - _TC"
+ s.insert()
+ s.submit()
+
+ # When Stock Entry has RM and FG
+ s = frappe.get_doc(_make_stock_entry(work_order.name, "Manufacture", 1))
+ s.save()
+ rm_cost = 0
+ for d in s.get('items'):
+ if d.s_warehouse:
+ rm_cost += d.amount
+ fg_cost = list(filter(lambda x: x.item_code=="_Test FG Item", s.get("items")))[0].amount
+ scrap_cost = list(filter(lambda x: x.is_scrap_item, s.get("items")))[0].amount
+ self.assertEqual(fg_cost,
+ flt(rm_cost - scrap_cost, 2))
+
+ # When Stock Entry has only FG + Scrap
+ s.items.pop(0)
+ s.items.pop(0)
+ s.submit()
+
+ rm_cost = 0
+ for d in s.get('items'):
+ if d.s_warehouse:
+ rm_cost += d.amount
+ self.assertEqual(rm_cost, 0)
+ expected_fg_cost = s.get_basic_rate_for_manufactured_item(1)
+ fg_cost = list(filter(lambda x: x.item_code=="_Test FG Item", s.get("items")))[0].amount
+ self.assertEqual(flt(fg_cost, 2), flt(expected_fg_cost, 2))
def test_variant_work_order(self):
bom_no = frappe.db.get_value("BOM", {"item": "_Test Variant Item",
@@ -618,8 +690,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 +896,116 @@
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)
+
+ @change_settings("Stock Settings", {"allow_negative_stock": 0})
+ def test_future_negative_sle(self):
+ # Initialize item, batch, warehouse, opening qty
+ item_code = '_Test Future Neg Item'
+ batch_no = '_Test Future Neg Batch'
+ warehouses = [
+ '_Test Future Neg Warehouse Source',
+ '_Test Future Neg Warehouse Destination'
+ ]
+ warehouse_names = initialize_records_for_future_negative_sle_test(
+ item_code, batch_no, warehouses,
+ opening_qty=2, posting_date='2021-07-01'
+ )
+
+ # Executing an illegal sequence should raise an error
+ sequence_of_entries = [
+ dict(item_code=item_code,
+ qty=2,
+ from_warehouse=warehouse_names[0],
+ to_warehouse=warehouse_names[1],
+ batch_no=batch_no,
+ posting_date='2021-07-03',
+ purpose='Material Transfer'),
+ dict(item_code=item_code,
+ qty=2,
+ from_warehouse=warehouse_names[1],
+ to_warehouse=warehouse_names[0],
+ batch_no=batch_no,
+ posting_date='2021-07-04',
+ purpose='Material Transfer'),
+ dict(item_code=item_code,
+ qty=2,
+ from_warehouse=warehouse_names[0],
+ to_warehouse=warehouse_names[1],
+ batch_no=batch_no,
+ posting_date='2021-07-02', # Illegal SE
+ purpose='Material Transfer')
+ ]
+
+ self.assertRaises(NegativeStockError, create_stock_entries, sequence_of_entries)
+
+ @change_settings("Stock Settings", {"allow_negative_stock": 0})
+ def test_future_negative_sle_batch(self):
+ from erpnext.stock.doctype.batch.test_batch import TestBatch
+
+ # Initialize item, batch, warehouse, opening qty
+ item_code = '_Test MultiBatch Item'
+ TestBatch.make_batch_item(item_code)
+
+ batch_nos = [] # store generate batches
+ warehouse = '_Test Warehouse - _TC'
+
+ se1 = make_stock_entry(
+ item_code=item_code,
+ qty=2,
+ to_warehouse=warehouse,
+ posting_date='2021-09-01',
+ purpose='Material Receipt'
+ )
+ batch_nos.append(se1.items[0].batch_no)
+ se2 = make_stock_entry(
+ item_code=item_code,
+ qty=2,
+ to_warehouse=warehouse,
+ posting_date='2021-09-03',
+ purpose='Material Receipt'
+ )
+ batch_nos.append(se2.items[0].batch_no)
+
+ with self.assertRaises(NegativeStockError) as nse:
+ make_stock_entry(item_code=item_code,
+ qty=1,
+ from_warehouse=warehouse,
+ batch_no=batch_nos[1],
+ posting_date='2021-09-02', # backdated consumption of 2nd batch
+ purpose='Material Issue')
+
def make_serialized_item(**args):
args = frappe._dict(args)
se = frappe.copy_doc(test_records[0])
@@ -894,3 +1076,31 @@
]
test_records = frappe.get_test_records('Stock Entry')
+
+def initialize_records_for_future_negative_sle_test(
+ item_code, batch_no, warehouses, opening_qty, posting_date):
+ from erpnext.stock.doctype.batch.test_batch import TestBatch, make_new_batch
+ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+ create_stock_reconciliation,
+ )
+ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+ TestBatch.make_batch_item(item_code)
+ make_new_batch(item_code=item_code, batch_id=batch_no)
+ warehouse_names = [create_warehouse(w) for w in warehouses]
+ create_stock_reconciliation(
+ purpose='Opening Stock',
+ posting_date=posting_date,
+ posting_time='20:00:20',
+ item_code=item_code,
+ warehouse=warehouse_names[0],
+ valuation_rate=100,
+ qty=opening_qty,
+ batch_no=batch_no,
+ )
+ return warehouse_names
+
+
+def create_stock_entries(sequence_of_entries):
+ for entry_detail in sequence_of_entries:
+ make_stock_entry(**entry_detail)
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_detail/__init__.py b/erpnext/stock/doctype/stock_entry_detail/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/stock_entry_detail/__init__.py
+++ b/erpnext/stock/doctype/stock_entry_detail/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 a5623fd..000ff2d 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class StockEntryDetail(Document):
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..efd97c0 100644
--- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
+++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..83ebe7e 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
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# 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/__init__.py b/erpnext/stock/doctype/stock_ledger_entry/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/__init__.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index 2463a21..2651407 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -142,6 +142,7 @@
"oldfieldtype": "Data",
"print_width": "150px",
"read_only": 1,
+ "search_index": 1,
"width": "150px"
},
{
@@ -316,7 +317,7 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-09-07 11:10:35.318872",
+ "modified": "2021-10-08 13:42:51.857631",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
@@ -338,4 +339,4 @@
],
"sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index be1f00e..c538307 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -1,16 +1,18 @@
-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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, 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 +29,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()
@@ -41,7 +43,6 @@
def on_submit(self):
self.check_stock_frozen_date()
- self.actual_amt_check()
self.calculate_batch_qty()
if not self.get("via_landed_cost_voucher"):
@@ -55,18 +56,6 @@
"sum(actual_qty)") or 0
frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
- def actual_amt_check(self):
- """Validate that qty at warehouse for selected batch is >=0"""
- if self.batch_no and not self.get("allow_negative_stock"):
- batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
- from `tabStock Ledger Entry`
- where is_cancelled =0 and warehouse=%s and item_code=%s and batch_no=%s""",
- (self.warehouse, self.item_code, self.batch_no))[0][0])
-
- if batch_bal_after_transaction < 0:
- frappe.throw(_("Stock balance in Batch {0} will become negative {1} for Item {2} at Warehouse {3}")
- .format(self.batch_no, batch_bal_after_transaction, self.item_code, self.warehouse))
-
def validate_mandatory(self):
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','company']
for k in mandatory:
@@ -177,4 +166,4 @@
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
- frappe.db.add_index("Stock Ledger Entry", ["voucher_detail_no"])
+ frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse")
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index af2ada8..cafbd75 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
@@ -1,23 +1,26 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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
from frappe.core.page.permission_manager.permission_manager import reset
+from frappe.utils import add_days, today
-class TestStockLedgerEntry(unittest.TestCase):
+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
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestStockLedgerEntry(ERPNextTestCase):
def setUp(self):
items = create_items()
reset('Stock Entry')
diff --git a/erpnext/stock/doctype/stock_reconciliation/__init__.py b/erpnext/stock/doctype/stock_reconciliation/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/stock_reconciliation/__init__.py
+++ b/erpnext/stock/doctype/stock_reconciliation/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index aa502a4..84f65a0 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -302,4 +302,3 @@
};
cur_frm.cscript = new erpnext.stock.StockReconciliation({frm: cur_frm});
-
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
index b7d1497..3402972 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "naming_series:",
"creation": "2013-03-28 10:35:31",
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
@@ -153,11 +154,12 @@
"icon": "fa fa-upload-alt",
"idx": 1,
"is_submittable": 1,
- "max_attachments": 1,
- "modified": "2020-04-08 17:02:47.196206",
+ "links": [],
+ "modified": "2021-11-30 01:33:51.437194",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index cda7c1d..82a8c37 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -1,16 +1,18 @@
# 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, 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 +161,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 +395,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 +618,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.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 94b006c..48e339a 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -4,22 +4,28 @@
# ERPNext - web based ERP (http://erpnext.com)
# 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 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 ERPNextTestCase, change_settings
-class TestStockReconciliation(unittest.TestCase):
+class TestStockReconciliation(ERPNextTestCase):
@classmethod
def setUpClass(self):
+ super().setUpClass()
create_batch_or_serial_no_items()
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@@ -310,6 +316,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 +327,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 +353,45 @@
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
+
+ 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")
+
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")
@@ -360,6 +399,34 @@
, do_not_submit=True)
self.assertRaises(frappe.ValidationError, sr.submit)
+ def test_serial_no_cancellation(self):
+
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+ item = create_item("Stock-Reco-Serial-Item-9", is_stock_item=1)
+ if not item.has_serial_no:
+ item.has_serial_no = 1
+ item.serial_no_series = "SRS9.####"
+ item.save()
+
+ item_code = item.name
+ warehouse = "_Test Warehouse - _TC"
+
+ se1 = make_stock_entry(item_code=item_code, target=warehouse, qty=10, basic_rate=700)
+
+ serial_nos = get_serial_nos(se1.items[0].serial_no)
+ # reduce 1 item
+ serial_nos.pop()
+ new_serial_nos = "\n".join(serial_nos)
+
+ sr = create_stock_reconciliation(item_code=item.name, warehouse=warehouse, serial_no=new_serial_nos, qty=9)
+ sr.cancel()
+
+ active_sr_no = frappe.get_all("Serial No",
+ filters={"item_code": item_code, "warehouse": warehouse, "status": "Active"})
+
+ self.assertEqual(len(active_sr_no), 10)
+
+
def create_batch_item_with_batch(item_name, batch_id):
batch_item_doc = create_item(item_name, is_stock_item=1)
if not batch_item_doc.has_batch_no:
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..b3b5d08 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
+
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%
copy from erpnext/healthcare/doctype/healthcare_settings/__init__.py
copy 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..0facae8
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -0,0 +1,80 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "beta": 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",
+ "item_based_reposting"
+ ],
+ "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"
+ },
+ {
+ "default": "0",
+ "fieldname": "item_based_reposting",
+ "fieldtype": "Check",
+ "label": "Use Item based reposting"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-11-02 01:22:45.155841",
+ "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 6167bec..cc0e2cf 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.js
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.js
@@ -15,4 +15,3 @@
frm.set_query("sample_retention_warehouse", filters);
}
});
-
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index f75cb56..33d9a6c 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -21,6 +21,7 @@
"mr_qty_allowance",
"column_break_12",
"auto_insert_price_list_rate_if_missing",
+ "update_existing_price_list_rate",
"allow_negative_stock",
"show_barcode_field",
"clean_description_html",
@@ -290,6 +291,13 @@
"fieldname": "mr_qty_allowance",
"fieldtype": "Float",
"label": "Over Transfer Allowance"
+ },
+ {
+ "default": "0",
+ "depends_on": "auto_insert_price_list_rate_if_missing",
+ "fieldname": "update_existing_price_list_rate",
+ "fieldtype": "Check",
+ "label": "Update Existing Price List Rate"
}
],
"icon": "icon-cog",
@@ -297,7 +305,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-06-28 17:02:26.683002",
+ "modified": "2021-11-06 19:40:02.183592",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",
@@ -317,4 +325,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 2dd7c6f..c1293cb 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -3,13 +3,16 @@
# 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
+
+from erpnext.stock.utils import check_pending_reposting
+
class StockSettings(Document):
def validate(self):
@@ -19,7 +22,7 @@
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
set_by_naming_series("Item", "item_code",
- self.get("item_naming_by")=="Naming Series", hide_name_field=True)
+ self.get("item_naming_by")=="Naming Series", hide_name_field=True, make_mandatory=0)
stock_frozen_limit = 356
submitted_stock_frozen = self.stock_frozen_upto_days or 0
@@ -35,6 +38,7 @@
self.validate_warehouses()
self.cant_change_valuation_method()
self.validate_clean_description_html()
+ self.validate_pending_reposts()
def validate_warehouses(self):
warehouse_fields = ["default_warehouse", "sample_retention_warehouse"]
@@ -63,6 +67,11 @@
# changed to text
frappe.enqueue('erpnext.stock.doctype.stock_settings.stock_settings.clean_all_descriptions', now=frappe.flags.in_test)
+ def validate_pending_reposts(self):
+ if self.stock_frozen_upto:
+ check_pending_reposting(self.stock_frozen_upto)
+
+
def on_update(self):
self.toggle_warehouse_field_for_inter_warehouse_transfer()
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..072b54b 100644
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
@@ -1,13 +1,16 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-import frappe
import unittest
-class TestStockSettings(unittest.TestCase):
+import frappe
+
+from erpnext.tests.utils import ERPNextTestCase
+
+
+class TestStockSettings(ERPNextTestCase):
def setUp(self):
+ super().setUp()
frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
def test_settings(self):
diff --git a/erpnext/stock/doctype/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..b33084a 100644
--- a/erpnext/stock/doctype/uom_category/test_uom_category.py
+++ b/erpnext/stock/doctype/uom_category/test_uom_category.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..844f6e6 100644
--- a/erpnext/stock/doctype/uom_category/uom_category.py
+++ b/erpnext/stock/doctype/uom_category/uom_category.py
@@ -1,9 +1,9 @@
-# -*- 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 UOMCategory(Document):
pass
diff --git a/erpnext/stock/doctype/uom_conversion_detail/__init__.py b/erpnext/stock/doctype/uom_conversion_detail/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/uom_conversion_detail/__init__.py
+++ b/erpnext/stock/doctype/uom_conversion_detail/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 fdead20..e17a01e 100644
--- a/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py
+++ b/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py
@@ -1,10 +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 frappe
from frappe.model.document import Document
+
class UOMConversionDetail(Document):
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..2c6b5f6 100644
--- a/erpnext/stock/doctype/variant_field/test_variant_field.py
+++ b/erpnext/stock/doctype/variant_field/test_variant_field.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..e8e02a0 100644
--- a/erpnext/stock/doctype/variant_field/variant_field.py
+++ b/erpnext/stock/doctype/variant_field/variant_field.py
@@ -1,9 +1,9 @@
-# -*- 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 VariantField(Document):
pass
diff --git a/erpnext/stock/doctype/warehouse/__init__.py b/erpnext/stock/doctype/warehouse/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/doctype/warehouse/__init__.py
+++ b/erpnext/stock/doctype/warehouse/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index 6e429a2..26db264 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -1,22 +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 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
+from erpnext.tests.utils import ERPNextTestCase
test_records = frappe.get_test_records('Warehouse')
-class TestWarehouse(unittest.TestCase):
+class TestWarehouse(ERPNextTestCase):
def setUp(self):
+ super().setUp()
if not frappe.get_value('Item', '_Test Item'):
make_test_records('Item')
@@ -34,65 +33,6 @@
self.assertEqual(p_warehouse.name, child_warehouse.parent_warehouse)
self.assertEqual(child_warehouse.is_group, 0)
- def test_warehouse_renaming(self):
- create_warehouse("Test Warehouse for Renaming 1", company="_Test Company with perpetual inventory")
- account = get_inventory_account("_Test Company with perpetual inventory", "Test Warehouse for Renaming 1 - TCP1")
- self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": account}))
-
- # Rename with abbr
- if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - TCP1"):
- frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - TCP1")
- frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 1 - TCP1", "Test Warehouse for Renaming 2 - TCP1")
-
- self.assertTrue(frappe.db.get_value("Warehouse",
- filters={"account": "Test Warehouse for Renaming 1 - TCP1"}))
-
- # Rename without abbr
- if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - TCP1"):
- frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - TCP1")
-
- frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 2 - TCP1", "Test Warehouse for Renaming 3")
-
- self.assertTrue(frappe.db.get_value("Warehouse",
- filters={"account": "Test Warehouse for Renaming 1 - TCP1"}))
-
- # Another rename with multiple dashes
- if frappe.db.exists("Warehouse", "Test - Warehouse - Company - TCP1"):
- frappe.delete_doc("Warehouse", "Test - Warehouse - Company - TCP1")
- frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 3 - TCP1", "Test - Warehouse - Company")
-
- def test_warehouse_merging(self):
- company = "_Test Company with perpetual inventory"
- create_warehouse("Test Warehouse for Merging 1", company=company,
- properties={"parent_warehouse": "All Warehouses - TCP1"})
- create_warehouse("Test Warehouse for Merging 2", company=company,
- properties={"parent_warehouse": "All Warehouses - TCP1"})
-
- make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 1 - TCP1",
- qty=1, rate=100, company=company)
- make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - TCP1",
- qty=1, rate=100, company=company)
-
- existing_bin_qty = (
- cint(frappe.db.get_value("Bin",
- {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - TCP1"}, "actual_qty"))
- + cint(frappe.db.get_value("Bin",
- {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - TCP1"}, "actual_qty"))
- )
-
- frappe.rename_doc("Warehouse", "Test Warehouse for Merging 1 - TCP1",
- "Test Warehouse for Merging 2 - TCP1", merge=True)
-
- self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - TCP1"))
-
- bin_qty = frappe.db.get_value("Bin",
- {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - TCP1"}, "actual_qty")
-
- self.assertEqual(bin_qty, existing_bin_qty)
-
- self.assertTrue(frappe.db.get_value("Warehouse",
- filters={"account": "Test Warehouse for Merging 2 - TCP1"}))
-
def test_unlinking_warehouse_from_item_defaults(self):
company = "_Test Company"
diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js
index 4e1679c4..9243e1e 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.js
+++ b/erpnext/stock/doctype/warehouse/warehouse.js
@@ -86,4 +86,3 @@
})
}
-
diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json
index 9b90932..05076b5 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.json
+++ b/erpnext/stock/doctype/warehouse/warehouse.json
@@ -1,7 +1,6 @@
{
"actions": [],
"allow_import": 1,
- "allow_rename": 1,
"creation": "2013-03-07 18:50:32",
"description": "A logical Warehouse against which stock entries are made.",
"doctype": "DocType",
@@ -245,7 +244,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
- "modified": "2021-04-09 19:54:56.263965",
+ "modified": "2021-12-03 04:40:06.414630",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index 3abc139..9cfad86 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -1,14 +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 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
+
+from erpnext.stock import get_warehouse_account
+
class Warehouse(NestedSet):
nsm_parent_field = 'parent_warehouse'
@@ -64,57 +67,6 @@
return frappe.db.sql("""select name from `tabWarehouse`
where parent_warehouse = %s limit 1""", self.name)
- def before_rename(self, old_name, new_name, merge=False):
- super(Warehouse, self).before_rename(old_name, new_name, merge)
-
- # Add company abbr if not provided
- new_warehouse = erpnext.encode_company_abbr(new_name, self.company)
-
- if merge:
- if not frappe.db.exists("Warehouse", new_warehouse):
- frappe.throw(_("Warehouse {0} does not exist").format(new_warehouse))
-
- if self.company != frappe.db.get_value("Warehouse", new_warehouse, "company"):
- frappe.throw(_("Both Warehouse must belong to same Company"))
-
- return new_warehouse
-
- def after_rename(self, old_name, new_name, merge=False):
- super(Warehouse, self).after_rename(old_name, new_name, merge)
-
- new_warehouse_name = self.get_new_warehouse_name_without_abbr(new_name)
- self.db_set("warehouse_name", new_warehouse_name)
-
- if merge:
- self.recalculate_bin_qty(new_name)
-
- def get_new_warehouse_name_without_abbr(self, name):
- company_abbr = frappe.get_cached_value('Company', self.company, "abbr")
- parts = name.rsplit(" - ", 1)
-
- if parts[-1].lower() == company_abbr.lower():
- name = parts[0]
-
- return name
-
- def recalculate_bin_qty(self, new_name):
- from erpnext.stock.stock_balance import repost_stock
- frappe.db.auto_commit_on_many_writes = 1
- existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
- frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
-
- repost_stock_for_items = frappe.db.sql_list("""select distinct item_code
- from tabBin where warehouse=%s""", new_name)
-
- # Delete all existing bins to avoid duplicate bins for the same item and warehouse
- frappe.db.sql("delete from `tabBin` where warehouse=%s", new_name)
-
- for item_code in repost_stock_for_items:
- repost_stock(item_code, new_name)
-
- frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
- frappe.db.auto_commit_on_many_writes = 0
-
def convert_to_group_or_ledger(self):
if self.is_group:
self.convert_to_ledger()
diff --git a/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py b/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py
index 39f4b23..273e795 100644
--- a/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py
+++ b/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
# 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..3e07fe7 100644
--- a/erpnext/stock/doctype/warehouse_type/warehouse_type.py
+++ b/erpnext/stock/doctype/warehouse_type/warehouse_type.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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/material_request/material_request.json b/erpnext/stock/form_tour/material_request/material_request.json
new file mode 100644
index 0000000..145b4a0
--- /dev/null
+++ b/erpnext/stock/form_tour/material_request/material_request.json
@@ -0,0 +1,97 @@
+{
+ "creation": "2021-07-29 12:32:08.929900",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:11:13.119453",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Material Request",
+ "owner": "Administrator",
+ "reference_doctype": "Material Request",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "The purpose of the material request can be selected here. For now select \"Purchase\" as the purpose.",
+ "field": "",
+ "fieldname": "material_request_type",
+ "fieldtype": "Select",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Purpose",
+ "next_step_condition": "eval: doc.material_request_type == \"Purchase\"",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Purpose"
+ },
+ {
+ "description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
+ "field": "",
+ "fieldname": "schedule_date",
+ "fieldtype": "Date",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Required By",
+ "next_step_condition": "",
+ "parent_field": "",
+ "position": "Left",
+ "title": "Required By"
+ },
+ {
+ "description": "Setting the target warehouse sets it for all the items.",
+ "field": "",
+ "fieldname": "set_warehouse",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Set Target Warehouse",
+ "next_step_condition": "",
+ "parent_field": "",
+ "position": "Left",
+ "title": "Target Warehouse"
+ },
+ {
+ "description": "Items table",
+ "field": "",
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Items",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Items"
+ },
+ {
+ "child_doctype": "Material Request Item",
+ "description": "Select an Item code. Item details will be fetched automatically.",
+ "field": "",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "has_next_condition": 1,
+ "is_table_field": 1,
+ "label": "Item Code",
+ "next_step_condition": "eval: doc.item_code",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Item Code"
+ },
+ {
+ "child_doctype": "Material Request Item",
+ "description": "Enter the required quantity for the material.",
+ "field": "",
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "has_next_condition": 0,
+ "is_table_field": 1,
+ "label": "Quantity",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Bottom",
+ "title": "Quantity"
+ }
+ ],
+ "title": "Material Request"
+}
\ No newline at end of file
diff --git a/erpnext/stock/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/get_item_details.py b/erpnext/stock/get_item_details.py
index c72073c..9889a22 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -1,23 +1,26 @@
# 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 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 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']
@@ -54,7 +57,7 @@
out = get_basic_details(args, item, overwrite_warehouse)
- if isinstance(doc, string_types):
+ if isinstance(doc, str):
doc = json.loads(doc)
if doc and doc.get('doctype') == 'Purchase Invoice':
@@ -84,10 +87,16 @@
out.update(get_bin_details(args.item_code, args.get("from_warehouse")))
elif out.get("warehouse"):
- out.update(get_bin_details(args.item_code, out.warehouse, args.company))
+ if doc and doc.get('doctype') == 'Purchase Order':
+ # calculate company_total_stock only for po
+ bin_details = get_bin_details(args.item_code, out.warehouse, args.company)
+ else:
+ bin_details = get_bin_details(args.item_code, out.warehouse)
+
+ out.update(bin_details)
# update args with out, if key or value not exists
- for key, value in iteritems(out):
+ for key, value in out.items():
if args.get(key) is None:
args[key] = value
@@ -152,7 +161,7 @@
def process_args(args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
args = frappe._dict(args)
@@ -169,7 +178,7 @@
return args
def process_string_args(args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
return args
@@ -290,7 +299,7 @@
"warehouse": warehouse,
"income_account": get_default_income_account(args, item_defaults, item_group_defaults, brand_defaults),
"expense_account": expense_account or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults) ,
- "discount_account": None or get_default_discount_account(args, item_defaults),
+ "discount_account": get_default_discount_account(args, item_defaults),
"cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults),
'has_serial_no': item.has_serial_no,
'has_batch_no': item.has_batch_no,
@@ -308,6 +317,7 @@
"net_rate": 0.0,
"net_amount": 0.0,
"discount_percentage": 0.0,
+ "discount_amount": 0.0,
"supplier": get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults),
"update_stock": args.get("update_stock") if args.get('doctype') in ['Sales Invoice', 'Purchase Invoice'] else 0,
"delivered_by_supplier": item.delivered_by_supplier if args.get("doctype") in ["Sales Order", "Sales Invoice"] else 0,
@@ -316,8 +326,9 @@
"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"),
+ "grant_commission": item.get("grant_commission")
})
if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
@@ -377,7 +388,7 @@
return out
-def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
+def get_item_warehouse(item, args, overwrite_warehouse, defaults=None):
if not defaults:
defaults = frappe._dict({
'item_defaults' : get_item_defaults(item.name, args.company),
@@ -480,8 +491,9 @@
"item_tax_template": None
}
"""
- item_tax_template = args.get("item_tax_template")
- item_tax_template = _get_item_tax_template(args, item.taxes, out)
+ item_tax_template = None
+ if item.taxes:
+ item_tax_template = _get_item_tax_template(args, item.taxes, out)
if not item_tax_template:
item_group = item.item_group
@@ -497,17 +509,17 @@
taxes_with_no_validity = []
for tax in taxes:
- tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company')
- if (tax.valid_from or tax.maximum_net_rate) and tax_company == args['company']:
- # In purchase Invoice first preference will be given to supplier invoice date
- # if supplier date is not present then posting date
- validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
+ tax_company = frappe.get_cached_value("Item Tax Template", tax.item_tax_template, 'company')
+ if tax_company == args['company']:
+ if (tax.valid_from or tax.maximum_net_rate):
+ # In purchase Invoice first preference will be given to supplier invoice date
+ # if supplier date is not present then posting date
+ validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
- if getdate(tax.valid_from) <= getdate(validation_date) \
- and is_within_valid_range(args, tax):
- taxes_with_validity.append(tax)
- else:
- if tax_company == args['company']:
+ if getdate(tax.valid_from) <= getdate(validation_date) \
+ and is_within_valid_range(args, tax):
+ taxes_with_validity.append(tax)
+ else:
taxes_with_no_validity.append(tax)
if taxes_with_validity:
@@ -696,7 +708,7 @@
{'item_code': args.item_code, 'price_list': args.price_list, 'currency': args.currency},
['name', 'price_list_rate'], as_dict=1)
if item_price and item_price.name:
- if item_price.price_list_rate != price_list_rate:
+ if item_price.price_list_rate != price_list_rate and frappe.db.get_single_value('Stock Settings', 'update_existing_price_list_rate'):
frappe.db.set_value('Item Price', item_price.name, "price_list_rate", price_list_rate)
frappe.msgprint(_("Item Price updated for {0} in Price List {1}").format(args.item_code,
args.price_list), alert=True)
@@ -885,8 +897,7 @@
res[fieldname] = pos_profile.get(fieldname)
if res.get("warehouse"):
- res.actual_qty = get_bin_details(args.item_code,
- res.warehouse).get("actual_qty")
+ res.actual_qty = get_bin_details(args.item_code, res.warehouse).get("actual_qty")
return res
@@ -1163,7 +1174,7 @@
@frappe.whitelist()
def get_serial_no(args, serial_nos=None, sales_order=None):
serial_no = None
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
args = frappe._dict(args)
if args.get('doctype') == 'Sales Invoice' and not args.get('update_stock'):
@@ -1192,7 +1203,7 @@
@frappe.whitelist()
def get_blanket_order_details(args):
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = frappe._dict(json.loads(args))
blanket_order_details = None
diff --git a/erpnext/stock/page/__init__.py b/erpnext/stock/page/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/stock/page/__init__.py
+++ b/erpnext/stock/page/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 de7e38e..adab478 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
@@ -1,19 +1,19 @@
{% for d in data %}
<div class="dashboard-list-item" style="padding: 7px 15px;">
<div class="row">
- <div class="col-sm-2 small" style="margin-top: 8px;">
+ <div class="col-sm-2" style="margin-top: 8px;">
<a data-type="warehouse" data-name="{{ d.warehouse }}">{{ d.warehouse }}</a>
</div>
- <div class="col-sm-2 small" style="margin-top: 8px; ">
+ <div class="col-sm-2" style="margin-top: 8px; ">
<a data-type="item" data-name="{{ d.item_code }}">{{ d.item_code }}</a>
</div>
- <div class="col-sm-1 small" style="margin-top: 8px; ">
+ <div class="col-sm-1" style="margin-top: 8px; ">
{{ d.stock_capacity }}
</div>
- <div class="col-sm-2 small" style="margin-top: 8px; ">
+ <div class="col-sm-2" style="margin-top: 8px; ">
{{ d.actual_qty }}
</div>
- <div class="col-sm-2 small">
+ <div class="col-sm-2">
<div class="progress" title="Occupied Qty: {{ d.actual_qty }}" style="margin-bottom: 4px; height: 7px; margin-top: 14px;">
<div class="progress-bar" role="progressbar"
aria-valuenow="{{ d.percent_occupied }}"
@@ -23,16 +23,19 @@
</div>
</div>
</div>
- <div class="col-sm-1 small" style="margin-top: 8px;">
+ <div class="col-sm-1" style="margin-top: 8px;">
{{ d.percent_occupied }}%
</div>
{% if can_write %}
- <div class="col-sm-1 text-right" style="margin-top: 2px;">
- <button class="btn btn-default btn-xs btn-edit"
- style="margin-top: 4px;margin-bottom: 4px;"
- data-warehouse="{{ d.warehouse }}"
- data-item="{{ escape(d.item_code) }}"
- data-company="{{ escape(d.company) }}">{{ __("Edit Capacity") }}</a>
+ <div class="col-sm-2 text-right" style="margin-top: 2px;">
+ <button
+ class="btn btn-default btn-xs btn-edit"
+ style="margin: 4px 0; float: left;"
+ data-warehouse="{{ d.warehouse }}"
+ data-item="{{ escape(d.item_code) }}"
+ data-company="{{ escape(d.company) }}">
+ {{ __("Edit Capacity") }}
+ </button>
</div>
{% endif %}
</div>
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
index c0ffdc9..ea27dd2 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -4,7 +4,7 @@
title: 'Warehouse Capacity Summary',
single_column: true
});
- page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'octicon octicon-sync');
+ page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'refresh');
page.start = 0;
page.company_field = page.add_field({
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 7ac5e64..1183ad4 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
@@ -1,18 +1,18 @@
<div class="dashboard-list-item" style="padding: 12px 15px;">
<div class="row">
- <div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+ <div class="col-sm-2 text-muted" style="margin-top: 8px;">
Warehouse
</div>
- <div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+ <div class="col-sm-2 text-muted" style="margin-top: 8px;">
Item
</div>
- <div class="col-sm-1 small text-muted" style="margin-top: 8px;">
+ <div class="col-sm-1 text-muted" style="margin-top: 8px;">
Stock Capacity
</div>
- <div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+ <div class="col-sm-2 text-muted" style="margin-top: 8px;">
Balance Stock Qty
</div>
- <div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+ <div class="col-sm-2 text-muted" style="margin-top: 8px;">
% Occupied
</div>
</div>
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 4c721ac..21f2573 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -1,12 +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
-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 +149,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 +175,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 29689b1..44e1386 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
@@ -1,10 +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 _
-from frappe.utils import flt, cint, getdate
+from frappe.utils import cint, getdate
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index 01927c2..9b21dea 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -1,7 +1,6 @@
# 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 import _
diff --git a/erpnext/stock/report/bom_search/bom_search.py b/erpnext/stock/report/bom_search/bom_search.py
index e3955c9..a22b224 100644
--- a/erpnext/stock/report/bom_search/bom_search.py
+++ b/erpnext/stock/report/bom_search/bom_search.py
@@ -1,10 +1,9 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe, json
-from six import iteritems
+import frappe
+
def execute(filters=None):
data = []
@@ -20,9 +19,9 @@
for d in frappe.get_all(doctype, fields=["parent", "item_code"]):
all_boms.setdefault(d.parent, []).append(d.item_code)
- for parent, items in iteritems(all_boms):
+ for parent, items in all_boms.items():
valid = True
- for key, item in iteritems(filters):
+ for key, item in filters.items():
if key != "search_sub_assemblies":
if item and item not in items:
valid = False
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 da593a4..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'
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 6130666..4ec36ea 100644
--- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py
+++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
@@ -1,11 +1,12 @@
# 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 date_diff
+
def execute(filters=None, consolidated = False):
data, columns = DelayedItemReport(filters).run()
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 d915160..26090ab 100644
--- a/erpnext/stock/report/delayed_order_report/delayed_order_report.py
+++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.py
@@ -1,10 +1,12 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 = [], []
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 77fd2ff..b7ac7ff 100644
--- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
+++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
@@ -1,10 +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
+
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/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 00125e7..6aa12ac 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,9 +3,9 @@
import frappe
from frappe import _
-from six import iteritems
from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
columns = get_columns()
@@ -25,7 +25,7 @@
def validate_data(itewise_balance_qty):
res = []
- for key, data in iteritems(itewise_balance_qty):
+ for key, data in itewise_balance_qty.items():
row = get_incorrect_data(data)
if row:
res.append(row)
@@ -46,7 +46,7 @@
return row
def get_stock_ledger_entries(report_filters):
- filters = {}
+ filters = {"is_cancelled": 0}
fields = ['name', 'voucher_type', 'voucher_no', 'item_code', 'actual_qty',
'posting_date', 'posting_time', 'company', 'warehouse', 'qty_after_transaction', 'batch_no']
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 b3b7594..d452ffd 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,14 @@
# 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()
@@ -40,7 +42,7 @@
total_value = frappe._dict({'qty': 0, 'valuation_rate': 0, 'serial_no': frappe.bold(_('Balance'))})
- for serial_no, data in iteritems(serial_nos_data):
+ for serial_no, data in serial_nos_data.items():
total_dict = frappe._dict({'qty': 0, 'valuation_rate': 0, 'serial_no': frappe.bold(_('Total'))})
if check_incorrect_serial_data(data, total_dict):
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 c8f60a1..28e6cb2 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
@@ -1,14 +1,15 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
-import erpnext
from frappe import _
-from six import iteritems
-from frappe.utils import add_days, today, getdate
-from erpnext.stock.utils import get_stock_value_on
+from frappe.utils import add_days, getdate, today
+
+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):
@@ -64,7 +65,7 @@
voucher_wise_dict.setdefault((d.item_code, d.warehouse), []).append(d)
closing_date = add_days(from_date, -1)
- for key, stock_data in iteritems(voucher_wise_dict):
+ for key, stock_data in voucher_wise_dict.items():
prev_stock_value = get_stock_value_on(posting_date = closing_date, item_code=key[0], warehouse =key[1])
for data in stock_data:
expected_stock_value = prev_stock_value + data.stock_value_difference
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..65af9f5 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,10 @@
# 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..0d0e8d2 100644
--- a/erpnext/stock/report/item_prices/item_prices.py
+++ b/erpnext/stock/report/item_prices/item_prices.py
@@ -1,11 +1,12 @@
# 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 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 c67eed7..30c7614 100644
--- a/erpnext/stock/report/item_shortage_report/item_shortage_report.py
+++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py
@@ -1,10 +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 = get_columns()
conditions = get_conditions(filters)
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..10cef70 100644
--- a/erpnext/stock/report/item_variant_details/item_variant_details.py
+++ b/erpnext/stock/report/item_variant_details/item_variant_details.py
@@ -1,10 +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 = get_columns(filters.item)
data = get_data(filters.item)
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..314f160 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
@@ -1,10 +1,10 @@
# 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 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/stock/report/product_bundle_balance/product_bundle_balance.py b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
index 8fffbcc..d9adced 100644
--- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
+++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
@@ -1,12 +1,12 @@
# 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 import _
from frappe.utils import flt
+
from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
-from six import iteritems
def execute(filters=None):
@@ -25,11 +25,11 @@
warehouse_company_map = {}
for child_item in required_items:
child_item_balance = stock_balance.get(child_item.item_code, frappe._dict())
- for warehouse, sle in iteritems(child_item_balance):
+ for warehouse, sle in child_item_balance.items():
if flt(sle.qty_after_transaction) > 0:
warehouse_company_map[warehouse] = sle.company
- for warehouse, company in iteritems(warehouse_company_map):
+ for warehouse, company in warehouse_company_map.items():
parent_row = {
"indent": 0,
"item_code": parent_item,
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 0d96ea6..9738442 100644
--- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
+++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
@@ -1,10 +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
+
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/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
index cc3aa35..80ec848 100644
--- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -1,10 +1,11 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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)
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.js b/erpnext/stock/report/stock_ageing/stock_ageing.js
index b22788f..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",
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 623dc2f..0ebe4f9 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -1,21 +1,24 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 six import iteritems
+from frappe.utils import cint, date_diff, flt
+
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):
+ for item, item_dict in item_details.items():
earliest_age, latest_age = 0, 0
fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func)
@@ -26,7 +29,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 +61,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 +202,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 +225,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.py b/erpnext/stock/report/stock_analytics/stock_analytics.py
index a1e1e7f..ddc8310 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.py
@@ -4,13 +4,18 @@
import frappe
from frappe import _, scrub
-from frappe.utils import getdate, get_quarter_start, get_first_day_of_week
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.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details)
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
+
def execute(filters=None):
is_reposting_item_valuation_in_progress()
filters = frappe._dict(filters or {})
diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
index 00e268b..32df585 100644
--- a/erpnext/stock/report/stock_analytics/test_stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -2,12 +2,13 @@
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
+from erpnext.tests.utils import ERPNextTestCase
-class TestStockAnalyticsReport(unittest.TestCase):
+class TestStockAnalyticsReport(ERPNextTestCase):
def test_get_period_date_ranges(self):
filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
diff --git a/erpnext/stock/report/stock_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 7e0c0e8..6fd3fe7 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
@@ -1,13 +1,15 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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.")
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..c0b89fd 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -1,16 +1,18 @@
# 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, 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 six import iteritems
+from operator import itemgetter
+
+import frappe
+from frappe import _
+from frappe.utils import cint, date_diff, flt, getdate
+
+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()
@@ -44,7 +46,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):
@@ -198,7 +200,9 @@
value_diff = flt(d.stock_value_difference)
- if d.posting_date < from_date:
+ if d.posting_date < from_date or (d.posting_date == from_date
+ and d.voucher_type == "Stock Reconciliation" and
+ frappe.db.get_value("Stock Reconciliation", d.voucher_no, "purpose") == "Opening Stock"):
qty_dict.opening_qty += qty_diff
qty_dict.opening_val += value_diff
@@ -223,7 +227,7 @@
qty_dict = iwb_map[(company, item, warehouse)]
no_transactions = True
- for key, val in iteritems(qty_dict):
+ for key, val in qty_dict.items():
val = flt(val, float_precision)
qty_dict[key] = val
if key != "val_rate" and val:
@@ -280,7 +284,7 @@
if filters.get('show_variant_attributes', 0) == 1:
variant_values = get_variant_values_for(list(item_details))
- item_details = {k: v.update(variant_values.get(k, {})) for k, v in iteritems(item_details)}
+ item_details = {k: v.update(variant_values.get(k, {})) for k, v in item_details.items()}
return item_details
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index b6923e9..c60a6ca 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -1,13 +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 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()
@@ -16,7 +20,7 @@
items = get_items(filters)
sl_entries = get_stock_ledger_entries(filters, items)
item_details = get_item_details(items, sl_entries, include_uom)
- opening_row = get_opening_balance(filters, columns)
+ opening_row = get_opening_balance(filters, columns, sl_entries)
precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
data = []
@@ -213,7 +217,7 @@
return "and {}".format(" and ".join(conditions)) if conditions else ""
-def get_opening_balance(filters, columns):
+def get_opening_balance(filters, columns, sl_entries):
if not (filters.item_code and filters.warehouse and filters.from_date):
return
@@ -225,6 +229,15 @@
"posting_time": "00:00:00"
})
+ # check if any SLEs are actually Opening Stock Reconciliation
+ for sle in sl_entries:
+ if (sle.get("voucher_type") == "Stock Reconciliation"
+ and sle.get("date").split()[0] == filters.from_date
+ and frappe.db.get_value("Stock Reconciliation", sle.voucher_no, "purpose") == "Opening Stock"
+ ):
+ last_entry = sle
+ sl_entries.remove(sle)
+
row = {
"item_code": _("'Opening'"),
"qty_after_transaction": last_entry.get("qty_after_transaction", 0),
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index 7956f2e..a28b752 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -1,12 +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 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()
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 fa19eeb..a7b4835 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
@@ -1,10 +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):
validate_warehouse(filters)
columns = get_columns()
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..d1748ed 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
@@ -1,11 +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
from frappe import _
from frappe.utils import flt
-from six import iteritems
+
def execute(filters=None):
columns = get_columns(filters)
@@ -14,7 +14,7 @@
material_transfer_vouchers = get_material_transfer_vouchers()
data = []
- for item_code, suppliers in iteritems(supplier_details):
+ for item_code, suppliers in supplier_details.items():
consumed_qty = consumed_amount = delivered_qty = delivered_amount = 0.0
total_qty = total_amount = 0.0
if consumed_details.get(item_code):
@@ -95,7 +95,7 @@
if supplier:
invalid_items = []
- for item_code, suppliers in iteritems(item_supplier_map):
+ for item_code, suppliers in item_supplier_map.items():
if supplier not in suppliers:
invalid_items.append(item_code)
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 90648f1..88054aa 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.js
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.js
@@ -10,23 +10,8 @@
"fieldtype": "Select",
"width": "80",
"reqd": 1,
- "options": ["", "Warehouse", "Company"],
- "change": function() {
- let group_by = frappe.query_report.get_filter_value("group_by")
- let company_filter = frappe.query_report.get_filter("company")
- if (group_by == "Company") {
- company_filter.df.reqd = 0;
- company_filter.df.hidden = 1;
- frappe.query_report.set_filter_value("company", "");
- company_filter.refresh();
- }
- else {
- company_filter.df.reqd = 1;
- company_filter.df.hidden = 0;
- company_filter.refresh();
- frappe.query_report.refresh();
- }
- }
+ "options": ["Warehouse", "Company"],
+ "default": "Warehouse",
},
{
"fieldname": "company",
@@ -34,8 +19,9 @@
"fieldtype": "Link",
"width": "80",
"options": "Company",
+ "reqd": 1,
"default": frappe.defaults.get_user_default("Company"),
- "reqd": 1
+ "depends_on": "eval: doc.group_by != 'Company'",
},
]
}
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..6f27558 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
@@ -1,13 +1,15 @@
# 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):
- if not filters: filters = {}
- validate_filters(filters)
+
+ if not filters:
+ filters = {}
columns = get_columns()
stock = get_total_stock(filters)
@@ -52,9 +54,3 @@
ON warehouse.name = ledger.warehouse
WHERE
ledger.actual_qty != 0 %s""" % (columns, conditions))
-
-def validate_filters(filters):
- if filters.get("group_by") == 'Company' and \
- filters.get("company"):
-
- frappe.throw(_("Please set Company filter blank if Group By is 'Company'"))
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..4d1491b 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
@@ -4,15 +4,20 @@
# Copyright (c) 2013, Tristar Enterprises and contributors
# 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 frappe.utils import flt
+
+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
-from six import iteritems
+
def execute(filters=None):
is_reposting_item_valuation_in_progress()
@@ -41,8 +46,8 @@
item_balance.setdefault((item, item_map[item]["item_group"]), [])
total_stock_value = 0.00
for wh in warehouse_list:
- row += [qty_dict.bal_qty] if wh.name in warehouse else [0.00]
- total_stock_value += qty_dict.bal_val if wh.name in warehouse else 0.00
+ row += [qty_dict.bal_qty] if wh.name == warehouse else [0.00]
+ total_stock_value += qty_dict.bal_val if wh.name == warehouse else 0.00
item_balance[(item, item_map[item]["item_group"])].append(row)
item_value.setdefault((item, item_map[item]["item_group"]),[])
@@ -50,7 +55,7 @@
# sum bal_qty by item
- for (item, item_group), wh_balance in iteritems(item_balance):
+ for (item, item_group), wh_balance in item_balance.items():
if not item_ageing.get(item): continue
total_stock_value = sum(item_value[(item, item_group)])
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index 8917bfe..6663458 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -1,12 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 +30,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:
@@ -159,7 +160,7 @@
def get_planned_qty(item_code, warehouse):
planned_qty = frappe.db.sql("""
select sum(qty - produced_qty) from `tabWork Order`
- where production_item = %s and fg_warehouse = %s and status not in ("Stopped", "Completed")
+ where production_item = %s and fg_warehouse = %s and status not in ("Stopped", "Completed", "Closed")
and docstatus=1 and qty > produced_qty""", (item_code, warehouse))
return flt(planned_qty[0][0]) if planned_qty else 0
@@ -247,5 +248,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..e95c0fc 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1,32 +1,32 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+
+import copy
+import json
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
-from six import iteritems
+from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, now, nowdate
+
+import erpnext
+from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
+from erpnext.stock.utils import (
+ get_incoming_outgoing_rate_for_cancel,
+ get_or_make_bin,
+ get_valuation_method,
+)
-# future reposting
class NegativeStockError(frappe.ValidationError): pass
class SerialNoExistsInFutureTransaction(frappe.ValidationError):
pass
_exceptions = frappe.local('stockledger_exceptions')
-# _exceptions = []
def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
from erpnext.controllers.stock_controller import future_sle_exists
if sl_entries:
- from erpnext.stock.utils import update_bin
-
cancel = sl_entries[0].get("is_cancelled")
if cancel:
validate_cancellation(sl_entries)
@@ -61,7 +61,38 @@
# preserve previous_qty_after_transaction for qty reposting
args.previous_qty_after_transaction = sle.get("previous_qty_after_transaction")
- update_bin(args, allow_negative_stock, via_landed_cost_voucher)
+ is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
+ if is_stock_item:
+ bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
+ update_bin_qty(bin_name, args)
+ repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher)
+ else:
+ frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
+
+def repost_current_voucher(args, allow_negative_stock=False, via_landed_cost_voucher=False):
+ if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
+ if not args.get("posting_date"):
+ args["posting_date"] = nowdate()
+
+ if args.get("is_cancelled") and via_landed_cost_voucher:
+ return
+
+ # Reposts only current voucher SL Entries
+ # Updates valuation rate, stock value, stock queue for current transaction
+ update_entries_after({
+ "item_code": args.get('item_code'),
+ "warehouse": args.get('warehouse'),
+ "posting_date": args.get("posting_date"),
+ "posting_time": args.get("posting_time"),
+ "voucher_type": args.get("voucher_type"),
+ "voucher_no": args.get("voucher_no"),
+ "sle_id": args.get('name'),
+ "creation": args.get('creation')
+ }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
+
+ # update qty in future sle and Validate negative qty
+ update_qty_in_future_sle(args, allow_negative_stock)
+
def get_args_for_future_sle(row):
return frappe._dict({
@@ -108,6 +139,7 @@
frappe.throw(_("Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet."))
if repost_entry.status == 'Queued':
doc = frappe.get_doc("Repost Item Valuation", repost_entry.name)
+ doc.flags.ignore_permissions = True
doc.cancel()
doc.delete()
@@ -118,12 +150,11 @@
(now(), frappe.session.user, voucher_type, voucher_no))
def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False):
- args.update({"doctype": "Stock Ledger Entry"})
+ args["doctype"] = "Stock Ledger Entry"
sle = frappe.get_doc(args)
sle.flags.ignore_permissions = 1
sle.allow_negative_stock=allow_negative_stock
sle.via_landed_cost_voucher = via_landed_cost_voucher
- sle.insert()
sle.submit()
return sle
@@ -149,7 +180,7 @@
distinct_item_warehouses[(args[i].get('item_code'), args[i].get('warehouse'))].reposting_status = True
if obj.new_items_found:
- for item_wh, data in iteritems(distinct_item_warehouses):
+ for item_wh, data in distinct_item_warehouses.items():
if ('args_idx' not in data and not data.reposting_status) or (data.sle_changed and data.reposting_status):
data.args_idx = len(args)
args.append(data.sle)
@@ -279,13 +310,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 +365,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 +433,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)
@@ -423,7 +458,7 @@
else:
self.get_fifo_values(sle)
self.wh_data.qty_after_transaction += flt(sle.actual_qty)
- self.wh_data.stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue))
+ self.wh_data.stock_value = sum(flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue)
# rounding as per precision
self.wh_data.stock_value = flt(self.wh_data.stock_value, self.precision)
@@ -439,7 +474,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 +511,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:
@@ -589,7 +627,7 @@
if not allow_zero_rate:
self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
- currency=erpnext.get_company_currency(sle.company))
+ currency=erpnext.get_company_currency(sle.company), company=sle.company)
def get_incoming_value_for_serial_nos(self, sle, serial_nos):
# get rate from serial nos within same company
@@ -656,7 +694,7 @@
if not allow_zero_valuation_rate:
self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
- currency=erpnext.get_company_currency(sle.company))
+ currency=erpnext.get_company_currency(sle.company), company=sle.company)
def get_fifo_values(self, sle):
incoming_rate = flt(sle.incoming_rate)
@@ -671,11 +709,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:
@@ -685,7 +727,7 @@
if not allow_zero_valuation_rate:
_rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
- currency=erpnext.get_company_currency(sle.company))
+ currency=erpnext.get_company_currency(sle.company), company=sle.company)
else:
_rate = 0
@@ -701,8 +743,8 @@
# If no entry found with outgoing rate, collapse stack
if index is None: # nosemgrep
- new_stock_value = sum((d[0]*d[1] for d in self.wh_data.stock_queue)) - qty_to_pop*outgoing_rate
- new_stock_qty = sum((d[0] for d in self.wh_data.stock_queue)) - qty_to_pop
+ new_stock_value = sum(d[0]*d[1] for d in self.wh_data.stock_queue) - qty_to_pop*outgoing_rate
+ new_stock_qty = sum(d[0] for d in self.wh_data.stock_queue) - qty_to_pop
self.wh_data.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]]
break
else:
@@ -726,8 +768,8 @@
batch[0] = batch[0] - qty_to_pop
qty_to_pop = 0
- stock_value = _round_off_if_near_zero(sum((flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue)))
- stock_qty = _round_off_if_near_zero(sum((flt(batch[0]) for batch in self.wh_data.stock_queue)))
+ stock_value = _round_off_if_near_zero(sum(flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue))
+ stock_qty = _round_off_if_near_zero(sum(flt(batch[0]) for batch in self.wh_data.stock_queue))
if stock_qty:
self.wh_data.valuation_rate = stock_value / flt(stock_qty)
@@ -760,7 +802,7 @@
def raise_exceptions(self):
msg_list = []
- for warehouse, exceptions in iteritems(self.exceptions):
+ for warehouse, exceptions in self.exceptions.items():
deficiency = min(e["diff"] for e in exceptions)
if ((exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]) in
@@ -788,15 +830,14 @@
def update_bin(self):
# update bin for each warehouse
- for warehouse, data in iteritems(self.data):
- bin_doc = get_bin(self.item_code, warehouse)
- bin_doc.update({
+ for warehouse, data in self.data.items():
+ bin_name = get_or_make_bin(self.item_code, warehouse)
+
+ frappe.db.set_value('Bin', bin_name, {
"valuation_rate": data.valuation_rate,
"actual_qty": data.qty_after_transaction,
"stock_value": data.stock_value
})
- bin_doc.flags.via_stock_ledger_entry = True
- bin_doc.save(ignore_permissions=True)
def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
@@ -897,12 +938,13 @@
def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True):
- # Get valuation rate from last sle for the same item and warehouse
- if not company:
- company = erpnext.get_default_company()
+ if not company:
+ company = frappe.get_cached_value("Warehouse", warehouse, "company")
+
+ # Get valuation rate from last sle for the same item and warehouse
last_valuation_rate = frappe.db.sql("""select valuation_rate
- from `tabStock Ledger Entry`
+ from `tabStock Ledger Entry` force index (item_warehouse)
where
item_code = %s
AND warehouse = %s
@@ -913,7 +955,7 @@
if not last_valuation_rate:
# Get valuation rate from last sle for the item against any warehouse
last_valuation_rate = frappe.db.sql("""select valuation_rate
- from `tabStock Ledger Entry`
+ from `tabStock Ledger Entry` force index (item_code)
where
item_code = %s
AND valuation_rate > 0
@@ -954,7 +996,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,21 +1085,40 @@
)
)"""
-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:
- sle = get_future_sle_with_negative_qty(args)
- if sle:
- message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
- abs(sle[0]["qty_after_transaction"]),
- frappe.get_desk_link('Item', args.item_code),
- frappe.get_desk_link('Warehouse', args.warehouse),
- sle[0]["posting_date"], sle[0]["posting_time"],
- frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"]))
+ if allow_negative_stock:
+ return
+ if not (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation"):
+ return
- frappe.throw(message, NegativeStockError, title='Insufficient Stock')
+ neg_sle = get_future_sle_with_negative_qty(args)
+ if neg_sle:
+ message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
+ abs(neg_sle[0]["qty_after_transaction"]),
+ frappe.get_desk_link('Item', args.item_code),
+ frappe.get_desk_link('Warehouse', args.warehouse),
+ neg_sle[0]["posting_date"], neg_sle[0]["posting_time"],
+ frappe.get_desk_link(neg_sle[0]["voucher_type"], neg_sle[0]["voucher_no"]))
+
+ frappe.throw(message, NegativeStockError, title='Insufficient Stock')
+
+
+ if not args.batch_no:
+ return
+
+ neg_batch_sle = get_future_sle_with_negative_batch_qty(args)
+ if neg_batch_sle:
+ message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
+ abs(neg_batch_sle[0]["cumulative_total"]),
+ frappe.get_desk_link('Batch', args.batch_no),
+ frappe.get_desk_link('Warehouse', args.warehouse),
+ neg_batch_sle[0]["posting_date"], neg_batch_sle[0]["posting_time"],
+ frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]))
+ frappe.throw(message, NegativeStockError, title="Insufficient Stock for Batch")
+
def get_future_sle_with_negative_qty(args):
return frappe.db.sql("""
@@ -1076,6 +1137,29 @@
limit 1
""", args, as_dict=1)
+
+def get_future_sle_with_negative_batch_qty(args):
+ return frappe.db.sql("""
+ with batch_ledger as (
+ select
+ posting_date, posting_time, voucher_type, voucher_no,
+ sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total
+ from `tabStock Ledger Entry`
+ where
+ item_code = %(item_code)s
+ and warehouse = %(warehouse)s
+ and batch_no=%(batch_no)s
+ and is_cancelled = 0
+ order by posting_date, posting_time, creation
+ )
+ select * from batch_ledger
+ where
+ cumulative_total < 0.0
+ and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
+ limit 1
+ """, args, as_dict=1)
+
+
def _round_off_if_near_zero(number: float, precision: int = 6) -> float:
""" Rounds off the number to zero only if number is close to zero for decimal
specified in precision. Precision defaults to 6.
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 9f6d0a8..3b1ae3b 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -1,15 +1,18 @@
# 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, erpnext
-from frappe import _
-import json
-from frappe.utils import flt, cstr, nowdate, nowtime, get_link_to_form
-from six import string_types
+import json
+
+import frappe
+from frappe import _
+from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
+
+import erpnext
+
class InvalidWarehouseCompany(frappe.ValidationError): pass
+class PendingRepostingError(frappe.ValidationError): pass
def get_stock_value_from_bin(warehouse=None, item_code=None):
values = {}
@@ -97,11 +100,7 @@
if with_valuation_rate:
if with_serial_no:
- serial_nos = last_entry.get("serial_no")
-
- if (serial_nos and
- len(get_serial_nos_data(serial_nos)) < last_entry.qty_after_transaction):
- serial_nos = get_serial_nos_data_after_transactions(args)
+ serial_nos = get_serial_nos_data_after_transactions(args)
return ((last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos)
if last_entry else (0.0, 0.0, 0.0))
@@ -111,19 +110,32 @@
return last_entry.qty_after_transaction if last_entry else 0.0
def get_serial_nos_data_after_transactions(args):
- serial_nos = []
- data = frappe.db.sql(""" SELECT serial_no, actual_qty
- FROM `tabStock Ledger Entry`
- WHERE
- item_code = %(item_code)s and warehouse = %(warehouse)s
- and timestamp(posting_date, posting_time) < timestamp(%(posting_date)s, %(posting_time)s)
- order by posting_date, posting_time asc """, args, as_dict=1)
+ from pypika import CustomFunction
- for d in data:
- if d.actual_qty > 0:
- serial_nos.extend(get_serial_nos_data(d.serial_no))
+ serial_nos = set()
+ args = frappe._dict(args)
+ sle = frappe.qb.DocType('Stock Ledger Entry')
+ Timestamp = CustomFunction('timestamp', ['date', 'time'])
+
+ stock_ledger_entries = frappe.qb.from_(
+ sle
+ ).select(
+ 'serial_no','actual_qty'
+ ).where(
+ (sle.item_code == args.item_code)
+ & (sle.warehouse == args.warehouse)
+ & (Timestamp(sle.posting_date, sle.posting_time) < Timestamp(args.posting_date, args.posting_time))
+ & (sle.is_cancelled == 0)
+ ).orderby(
+ sle.posting_date, sle.posting_time, sle.creation
+ ).run(as_dict=1)
+
+ for stock_ledger_entry in stock_ledger_entries:
+ changed_serial_no = get_serial_nos_data(stock_ledger_entry.serial_no)
+ if stock_ledger_entry.actual_qty > 0:
+ serial_nos.update(changed_serial_no)
else:
- serial_nos = list(set(serial_nos) - set(get_serial_nos_data(d.serial_no)))
+ serial_nos.difference_update(changed_serial_no)
return '\n'.join(serial_nos)
@@ -176,12 +188,28 @@
bin_obj.flags.ignore_permissions = True
return bin_obj
+def get_or_make_bin(item_code: str , warehouse: str) -> str:
+ bin_record = frappe.db.get_value('Bin', {'item_code': item_code, 'warehouse': warehouse})
+
+ if not bin_record:
+ bin_obj = frappe.get_doc({
+ "doctype": "Bin",
+ "item_code": item_code,
+ "warehouse": warehouse,
+ })
+ bin_obj.flags.ignore_permissions = 1
+ bin_obj.insert()
+ bin_record = bin_obj.name
+
+ return bin_record
+
def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
+ """WARNING: This function is deprecated. Inline this function instead of using it."""
+ from erpnext.stock.doctype.bin.bin import update_stock
is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
if is_stock_item:
- bin = get_bin(args.get("item_code"), args.get("warehouse"))
- bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
- return bin
+ bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
+ update_stock(bin_name, args, allow_negative_stock, via_landed_cost_voucher)
else:
frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
@@ -189,7 +217,7 @@
def get_incoming_rate(args, raise_error_if_no_rate=True):
"""Get Incoming Rate based on valuation method"""
from erpnext.stock.stock_ledger import get_previous_sle, get_valuation_rate
- if isinstance(args, string_types):
+ if isinstance(args, str):
args = json.loads(args)
in_rate = 0
@@ -390,3 +418,28 @@
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
if reposting_in_progress:
frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1)
+
+def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool:
+ """Check if there are pending reposting job till the specified posting date."""
+
+ filters = {
+ "docstatus": 1,
+ "status": ["in", ["Queued","In Progress", "Failed"]],
+ "posting_date": ["<=", posting_date],
+ }
+
+ reposting_pending = frappe.db.exists("Repost Item Valuation", filters)
+ if reposting_pending and throw_error:
+ msg = _("Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later.")
+ frappe.msgprint(msg,
+ raise_exception=PendingRepostingError,
+ title="Stock Reposting Ongoing",
+ indicator="red",
+ primary_action={
+ "label": _("Show pending entries"),
+ "client_action": "erpnext.route_to_pending_reposts",
+ "args": filters,
+ }
+ )
+
+ return bool(reposting_pending)
diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json
index 26d10ce..4df27f5 100644
--- a/erpnext/stock/workspace/stock/stock.json
+++ b/erpnext/stock/workspace/stock/stock.json
@@ -1,6 +1,4 @@
{
- "cards_label": "Masters & Reports",
- "category": "",
"charts": [
{
"chart_name": "Warehouse wise Stock Value"
@@ -8,18 +6,12 @@
],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Stock\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Receipt\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Delivery Note\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Masters & Reports\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Transactions\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Serial No and Batch\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Incorrect Data Report\", \"col\": 4}}]",
"creation": "2020-03-02 15:43:10.096528",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "stock",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Stock",
"links": [
{
@@ -712,67 +704,14 @@
"link_type": "Report",
"onboard": 0,
"type": "Link"
- },
- {
- "dependencies": "Stock Ledger Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Stock and Account Value Comparison",
- "link_count": 0,
- "link_to": "Stock and Account Value Comparison",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Incorrect Data Report",
- "link_count": 0,
- "link_type": "DocType",
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Incorrect Serial No Qty and Valuation",
- "link_count": 0,
- "link_to": "Incorrect Serial No Valuation",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Incorrect Balance Qty After Transaction",
- "link_count": 0,
- "link_to": "Incorrect Balance Qty After Transaction",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Stock and Account Value Comparison",
- "link_count": 0,
- "link_to": "Stock and Account Value Comparison",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
}
],
- "modified": "2021-08-05 12:16:02.361509",
+ "modified": "2021-11-23 04:34:00.420870",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock",
- "onboarding": "Stock",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
@@ -831,6 +770,5 @@
"type": "Dashboard"
}
],
- "shortcuts_label": "Quick Access",
"title": "Stock"
}
\ No newline at end of file
diff --git a/erpnext/support/__init__.py b/erpnext/support/__init__.py
index cc26c14..ac23ede 100644
--- a/erpnext/support/__init__.py
+++ b/erpnext/support/__init__.py
@@ -1,4 +1,3 @@
-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/__init__.py b/erpnext/support/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/support/doctype/__init__.py
+++ b/erpnext/support/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index f28976e..0dc3639 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -1,17 +1,19 @@
# 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 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):
@@ -225,7 +227,7 @@
def set_first_response_time(communication, method):
if communication.get('reference_doctype') == "Issue":
issue = get_parent_doc(communication)
- if is_first_response(issue):
+ if is_first_response(issue) and issue.service_level_agreement:
first_response_time = calculate_first_response_time(issue, get_datetime(issue.first_responded_on))
issue.db_set("first_response_time", first_response_time)
diff --git a/erpnext/support/doctype/issue/issue_dashboard.py b/erpnext/support/doctype/issue/issue_dashboard.py
index 2ac7c81..7ab358a 100644
--- a/erpnext/support/doctype/issue/issue_dashboard.py
+++ b/erpnext/support/doctype/issue/issue_dashboard.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
from frappe import _
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 4146e48..ab9a444b 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -1,14 +1,17 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import datetime
+import unittest
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
+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):
@@ -22,58 +25,58 @@
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()
@@ -81,21 +84,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()
@@ -105,33 +108,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()
diff --git a/erpnext/support/doctype/issue_priority/issue_priority.py b/erpnext/support/doctype/issue_priority/issue_priority.py
index 514b6cc..f21a453 100644
--- a/erpnext/support/doctype/issue_priority/issue_priority.py
+++ b/erpnext/support/doctype/issue_priority/issue_priority.py
@@ -1,11 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 IssuePriority(Document):
pass
diff --git a/erpnext/support/doctype/issue_priority/test_issue_priority.py b/erpnext/support/doctype/issue_priority/test_issue_priority.py
index 618c93e..d2b1415 100644
--- a/erpnext/support/doctype/issue_priority/test_issue_priority.py
+++ b/erpnext/support/doctype/issue_priority/test_issue_priority.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
+
class TestIssuePriority(unittest.TestCase):
diff --git a/erpnext/support/doctype/issue_type/issue_type.py b/erpnext/support/doctype/issue_type/issue_type.py
index f95d09c..c5adc8b 100644
--- a/erpnext/support/doctype/issue_type/issue_type.py
+++ b/erpnext/support/doctype/issue_type/issue_type.py
@@ -1,10 +1,9 @@
-# -*- 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.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..a362034 100644
--- a/erpnext/support/doctype/issue_type/test_issue_type.py
+++ b/erpnext/support/doctype/issue_type/test_issue_type.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# 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..41d4f7f 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class 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..4a3b1f0 100644
--- a/erpnext/support/doctype/service_day/service_day.py
+++ b/erpnext/support/doctype/service_day/service_day.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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 b649b87..5f470aa 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -203,10 +203,11 @@
}
],
"links": [],
- "modified": "2021-07-27 11:16:45.596579",
+ "modified": "2021-10-02 11:32:55.556024",
"modified_by": "Administrator",
"module": "Support",
"name": "Service Level Agreement",
+ "naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
@@ -237,4 +238,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
+}
\ No newline at end of file
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 8c1c1ef..5f8f83d 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -1,19 +1,31 @@
-# -*- 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 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, nowdate
-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
-from frappe.utils.safe_exec import get_safe_globals
+
class ServiceLevelAgreement(Document):
def validate(self):
@@ -325,7 +337,7 @@
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
@@ -846,7 +858,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 7e7a405..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',
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 d9c671e..cfbe744 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
@@ -1,14 +1,17 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import datetime
+import unittest
import frappe
-import unittest
-import datetime
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):
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..adb153e 100644
--- a/erpnext/support/doctype/service_level_priority/service_level_priority.py
+++ b/erpnext/support/doctype/service_level_priority/service_level_priority.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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..2270015 100644
--- a/erpnext/support/doctype/support_search_source/support_search_source.py
+++ b/erpnext/support/doctype/support_search_source/support_search_source.py
@@ -1,9 +1,9 @@
-# -*- 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 SupportSearchSource(Document):
pass
diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json
index 5d3d3ac..bf1daa1 100644
--- a/erpnext/support/doctype/support_settings/support_settings.json
+++ b/erpnext/support/doctype/support_settings/support_settings.json
@@ -37,7 +37,6 @@
},
{
"default": "7",
- "description": "Auto close Issue after 7 days",
"fieldname": "close_issue_after_days",
"fieldtype": "Int",
"label": "Close Issue After Days"
@@ -164,7 +163,7 @@
],
"issingle": 1,
"links": [],
- "modified": "2020-06-11 13:08:38.473616",
+ "modified": "2021-10-14 13:08:38.473616",
"modified_by": "Administrator",
"module": "Support",
"name": "Support Settings",
@@ -185,4 +184,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/support/doctype/support_settings/support_settings.py b/erpnext/support/doctype/support_settings/support_settings.py
index bb3c53a..ee8a3f5 100644
--- a/erpnext/support/doctype/support_settings/support_settings.py
+++ b/erpnext/support/doctype/support_settings/support_settings.py
@@ -1,10 +1,9 @@
-# -*- 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.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..4eaf532 100644
--- a/erpnext/support/doctype/support_settings/test_support_settings.py
+++ b/erpnext/support/doctype/support_settings/test_support_settings.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
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..f022d55 100644
--- a/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
+++ b/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
-from __future__ import unicode_literals
+
+import unittest
import frappe
-import unittest
test_records = frappe.get_test_records('Warranty Claim')
diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.json b/erpnext/support/doctype/warranty_claim/warranty_claim.json
index 88ee4a3..45485ca 100644
--- a/erpnext/support/doctype/warranty_claim/warranty_claim.json
+++ b/erpnext/support/doctype/warranty_claim/warranty_claim.json
@@ -256,6 +256,7 @@
"fieldname": "contact_email",
"fieldtype": "Data",
"label": "Contact Email",
+ "options": "Email",
"read_only": 1
},
{
@@ -361,7 +362,7 @@
],
"icon": "fa fa-bug",
"idx": 1,
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-11-09 17:26:09.703215",
"modified_by": "Administrator",
"module": "Support",
"name": "Warranty Claim",
@@ -385,4 +386,4 @@
"sort_order": "DESC",
"timeline_field": "customer",
"title_field": "customer_name"
-}
\ No newline at end of file
+}
diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.py b/erpnext/support/doctype/warranty_claim/warranty_claim.py
index a20e7a8..87e9541 100644
--- a/erpnext/support/doctype/warranty_claim/warranty_claim.py
+++ b/erpnext/support/doctype/warranty_claim/warranty_claim.py
@@ -2,15 +2,14 @@
# License: GNU General Public License v3. See license.txt
-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/page/__init__.py b/erpnext/support/page/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/support/page/__init__.py
+++ b/erpnext/support/page/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
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 69bf273..2ab0fb8 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
@@ -1,9 +1,10 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
+
def execute(filters=None):
columns = [
{
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.py b/erpnext/support/report/issue_analytics/issue_analytics.py
index 54fce0b..056f2e0 100644
--- a/erpnext/support/report/issue_analytics/issue_analytics.py
+++ b/erpnext/support/report/issue_analytics/issue_analytics.py
@@ -1,14 +1,16 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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 erpnext.accounts.utils import get_fiscal_year
+
def execute(filters=None):
return IssueAnalytics(filters).run()
@@ -103,7 +105,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 = {
@@ -167,7 +169,7 @@
self.data = []
self.get_periodic_data()
- for entity, period_data in iteritems(self.issue_periodic_data):
+ for entity, period_data in self.issue_periodic_data.items():
if self.filters.based_on == 'Customer':
row = {'customer': entity}
elif self.filters.based_on == 'Assigned To':
diff --git a/erpnext/support/report/issue_analytics/test_issue_analytics.py b/erpnext/support/report/issue_analytics/test_issue_analytics.py
index a9d961a..ba4dc54 100644
--- a/erpnext/support/report/issue_analytics/test_issue_analytics.py
+++ b/erpnext/support/report/issue_analytics/test_issue_analytics.py
@@ -1,11 +1,14 @@
-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']
diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py
index 7c4af39..39a5c40 100644
--- a/erpnext/support/report/issue_summary/issue_summary.py
+++ b/erpnext/support/report/issue_summary/issue_summary.py
@@ -1,13 +1,14 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# 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
+
def execute(filters=None):
return IssueSummary(filters).run()
@@ -139,7 +140,7 @@
self.data = []
self.get_summary_data()
- for entity, data in iteritems(self.issue_summary_data):
+ for entity, data in self.issue_summary_data.items():
if self.filters.based_on == 'Customer':
row = {'customer': entity}
elif self.filters.based_on == 'Assigned To':
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..6b2098f 100644
--- a/erpnext/support/report/support_hour_distribution/support_hour_distribution.py
+++ b/erpnext/support/report/support_hour_distribution/support_hour_distribution.py
@@ -1,11 +1,10 @@
# 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 add_to_date, getdate, get_datetime
-from six import iteritems
+from frappe.utils import add_to_date, get_datetime, getdate
time_slots = {
'12AM - 3AM': '00:00:00-03:00:00',
@@ -34,7 +33,7 @@
time_slot_wise_total_count = {}
while(start_date <= getdate(filters.to_date)):
hours_count = {'date': start_date}
- for key, value in iteritems(time_slots):
+ for key, value in time_slots.items():
start_time, end_time = value.split('-')
start_time = get_datetime("{0} {1}".format(start_date.strftime("%Y-%m-%d"), start_time))
end_time = get_datetime("{0} {1}".format(start_date.strftime("%Y-%m-%d"), end_time))
diff --git a/erpnext/support/web_form/issues/issues.py b/erpnext/support/web_form/issues/issues.py
index 2334f8b..02e3e93 100644
--- a/erpnext/support/web_form/issues/issues.py
+++ b/erpnext/support/web_form/issues/issues.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
pass
diff --git a/erpnext/support/workspace/support/support.json b/erpnext/support/workspace/support/support.json
index 4c5829d..d68c7c7 100644
--- a/erpnext/support/workspace/support/support.json
+++ b/erpnext/support/workspace/support/support.json
@@ -1,20 +1,13 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Issue\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Maintenance Visit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Issues\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Warranty\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:48:23.224699",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"icon": "support",
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Support",
"links": [
{
@@ -176,15 +169,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:02.699923",
+ "modified": "2021-08-05 12:16:02.699924",
"modified_by": "Administrator",
"module": "Support",
"name": "Support",
- "onboarding": "",
"owner": "Administrator",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py
index 6f8e411..0c24484 100644
--- a/erpnext/telephony/doctype/call_log/call_log.py
+++ b/erpnext/telephony/doctype/call_log/call_log.py
@@ -1,16 +1,15 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 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']
diff --git a/erpnext/telephony/doctype/call_log/test_call_log.py b/erpnext/telephony/doctype/call_log/test_call_log.py
index faa6304..111c7a7 100644
--- a/erpnext/telephony/doctype/call_log/test_call_log.py
+++ b/erpnext/telephony/doctype/call_log/test_call_log.py
@@ -1,10 +1,9 @@
-# -*- 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 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..b73f385 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
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class IncomingCallHandlingSchedule(Document):
pass
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..08e244d 100644
--- a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py
+++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
+
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..28f7f43 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
@@ -1,10 +1,9 @@
-# -*- 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 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..97aa56f 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
@@ -1,10 +1,9 @@
-# -*- 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 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..9f9486f 100644
--- a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py
+++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py
@@ -1,10 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class VoiceCallSettings(Document):
pass
diff --git a/erpnext/templates/emails/anniversary_reminder.html b/erpnext/templates/emails/anniversary_reminder.html
index ac9f7e4..db338dd 100644
--- a/erpnext/templates/emails/anniversary_reminder.html
+++ b/erpnext/templates/emails/anniversary_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/holiday_reminder.html b/erpnext/templates/emails/holiday_reminder.html
index e38d27b..bbef6be 100644
--- a/erpnext/templates/emails/holiday_reminder.html
+++ b/erpnext/templates/emails/holiday_reminder.html
@@ -11,6 +11,6 @@
{% endfor %}
</ol>
{% else %}
- <p>You don't have no upcoming holidays this {{ frequency }}.</p>
+ <p>You have no upcoming holidays this {{ frequency }}.</p>
{% endif %}
{% endif %}
diff --git a/erpnext/templates/includes/salary_slip_log.html b/erpnext/templates/includes/salary_slip_log.html
index d36ee6e..22c62ce 100644
--- a/erpnext/templates/includes/salary_slip_log.html
+++ b/erpnext/templates/includes/salary_slip_log.html
@@ -10,7 +10,7 @@
<tbody>
{% for ss_dict in ss_list %}
<tr>
- {% for key, value in ss_dict.iteritems()|sort %}
+ {% for key, value in ss_dict.items()|sort %}
<td {% if key == "Total Pay"%} align = "right" {% endif %}> {{value}} </td>
{% endfor %}
</tr>
diff --git a/erpnext/templates/pages/cart.py b/erpnext/templates/pages/cart.py
index 30b0357..0bba1ff 100644
--- a/erpnext/templates/pages/cart.py
+++ b/erpnext/templates/pages/cart.py
@@ -1,11 +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
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/courses.py b/erpnext/templates/pages/courses.py
index 92c38f6..6051e60 100644
--- a/erpnext/templates/pages/courses.py
+++ b/erpnext/templates/pages/courses.py
@@ -1,9 +1,8 @@
# 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 frappe
def get_context(context):
diff --git a/erpnext/templates/pages/help.py b/erpnext/templates/pages/help.py
index 4ce2b31..6a83fc8 100644
--- a/erpnext/templates/pages/help.py
+++ b/erpnext/templates/pages/help.py
@@ -1,8 +1,9 @@
-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.py b/erpnext/templates/pages/home.py
index 1c14450..5d046a8 100644
--- a/erpnext/templates/pages/home.py
+++ b/erpnext/templates/pages/home.py
@@ -1,7 +1,7 @@
# 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
no_cache = 1
diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.py b/erpnext/templates/pages/integrations/gocardless_checkout.py
index bdef79c..bbdbf1d 100644
--- a/erpnext/templates/pages/integrations/gocardless_checkout.py
+++ b/erpnext/templates/pages/integrations/gocardless_checkout.py
@@ -1,12 +1,16 @@
# 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
diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.py b/erpnext/templates/pages/integrations/gocardless_confirmation.py
index 0b72e9f..a6c3e71 100644
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.py
+++ b/erpnext/templates/pages/integrations/gocardless_confirmation.py
@@ -1,9 +1,13 @@
# 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
diff --git a/erpnext/templates/pages/material_request_info.py b/erpnext/templates/pages/material_request_info.py
index e29860d..65d4427 100644
--- a/erpnext/templates/pages/material_request_info.py
+++ b/erpnext/templates/pages/material_request_info.py
@@ -1,12 +1,12 @@
# 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 import _
-
from frappe.utils import flt
+
def get_context(context):
context.no_cache = 1
context.show_sidebar = True
diff --git a/erpnext/templates/pages/non_profit/join_chapter.py b/erpnext/templates/pages/non_profit/join_chapter.py
index aa54a58..7caf87d 100644
--- a/erpnext/templates/pages/non_profit/join_chapter.py
+++ b/erpnext/templates/pages/non_profit/join_chapter.py
@@ -1,6 +1,6 @@
-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.py b/erpnext/templates/pages/non_profit/leave_chapter.py
index 21cb722..65908e1 100644
--- a/erpnext/templates/pages/non_profit/leave_chapter.py
+++ b/erpnext/templates/pages/non_profit/leave_chapter.py
@@ -1,6 +1,6 @@
-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 816a259..2aa0f9c 100644
--- a/erpnext/templates/pages/order.py
+++ b/erpnext/templates/pages/order.py
@@ -1,11 +1,14 @@
# 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 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
@@ -36,7 +39,9 @@
# check for the loyalty program of the customer
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..e4043ea 100644
--- a/erpnext/templates/pages/partners.py
+++ b/erpnext/templates/pages/partners.py
@@ -1,7 +1,7 @@
# 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
page_title = "Partners"
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
index 9ab76de..5aa1f1e 100644
--- a/erpnext/templates/pages/product_search.py
+++ b/erpnext/templates/pages/product_search.py
@@ -1,9 +1,10 @@
# 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 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
diff --git a/erpnext/templates/pages/projects.py b/erpnext/templates/pages/projects.py
index 7ff4954..16aa439 100644
--- a/erpnext/templates/pages/projects.py
+++ b/erpnext/templates/pages/projects.py
@@ -1,9 +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 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..95b8f72 100644
--- a/erpnext/templates/pages/regional/india/update_gstin.py
+++ b/erpnext/templates/pages/regional/india/update_gstin.py
@@ -1,7 +1,5 @@
-from __future__ import unicode_literals
import frappe
-from frappe import _
-from six import iteritems
+
def get_context(context):
context.no_cache = 1
@@ -30,7 +28,7 @@
def update_gstin(context):
dirty = False
- for key, value in iteritems(frappe.form_dict):
+ for key, value in frappe.form_dict.items():
if key != 'party':
address_name = frappe.get_value('Address', key)
if address_name:
diff --git a/erpnext/templates/pages/rfq.py b/erpnext/templates/pages/rfq.py
index 67679a1..0afd46c 100644
--- a/erpnext/templates/pages/rfq.py
+++ b/erpnext/templates/pages/rfq.py
@@ -1,12 +1,14 @@
# 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 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..1ef3942 100644
--- a/erpnext/templates/pages/search_help.py
+++ b/erpnext/templates/pages/search_help.py
@@ -1,11 +1,11 @@
-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
+
def get_context(context):
context.no_cache = 1
@@ -74,7 +74,7 @@
for topic in topics_data:
route = api.base_url + '/' + (api.post_route + '/' if api.post_route else "")
for key in api.post_route_key_list.split(','):
- route += text_type(topic[key])
+ route += str(topic[key])
results.append(frappe._dict({
'title': topic[api.post_title_key],
diff --git a/erpnext/templates/pages/task_info.py b/erpnext/templates/pages/task_info.py
index 260e278..d1a70e1 100644
--- a/erpnext/templates/pages/task_info.py
+++ b/erpnext/templates/pages/task_info.py
@@ -1,7 +1,5 @@
-from __future__ import unicode_literals
import frappe
-from frappe import _
def get_context(context):
context.no_cache = 1
diff --git a/erpnext/templates/pages/timelog_info.py b/erpnext/templates/pages/timelog_info.py
index ee86483..db61e7e 100644
--- a/erpnext/templates/pages/timelog_info.py
+++ b/erpnext/templates/pages/timelog_info.py
@@ -1,7 +1,5 @@
-from __future__ import unicode_literals
import frappe
-from frappe import _
def get_context(context):
context.no_cache = 1
diff --git a/erpnext/templates/print_formats/includes/taxes_and_charges.html b/erpnext/templates/print_formats/includes/taxes_and_charges.html
new file mode 100644
index 0000000..0d8e383
--- /dev/null
+++ b/erpnext/templates/print_formats/includes/taxes_and_charges.html
@@ -0,0 +1,34 @@
+{% macro render_row(label, value) %}
+<div class="field row">
+ <div class="col-7 {%- if doc.align_labels_right %} text-right{%- endif -%}">
+ <div class="label">{{ label }}</div>
+ </div>
+ <div class="text-right col-5">
+ {{ value }}
+ </div>
+</div>
+{% endmacro %}
+
+{%- macro render_discount_amount(doc) -%}
+ {%- if doc.discount_amount -%}
+ {{ render_row(_(doc.meta.get_label('discount_amount')), '- ' + doc.get_formatted("discount_amount", doc)) }}
+ {%- endif -%}
+{%- endmacro -%}
+
+<div class="row">
+ <div class="col"></div>
+ <div class="col">
+ {%- if doc.apply_discount_on == "Net Total" -%}
+ {{ render_discount_amount(doc) }}
+ {%- endif -%}
+ {%- for charge in doc.taxes -%}
+ {%- if (charge.tax_amount or print_settings.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
+ {{ render_row(charge.get_formatted("description"), charge.get_formatted('tax_amount', doc)) }}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- if doc.apply_discount_on == "Grand Total" -%}
+ {{ render_discount_amount(doc) }}
+ {%- endif -%}
+ </div>
+</div>
+
diff --git a/erpnext/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/templates/utils.py b/erpnext/templates/utils.py
index 743657d..9f46e6a 100644
--- a/erpnext/templates/utils.py
+++ b/erpnext/templates/utils.py
@@ -1,7 +1,6 @@
# 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
diff --git a/erpnext/tests/test_init.py b/erpnext/tests/test_init.py
index abc04a8..36a9bf5 100644
--- a/erpnext/tests/test_init.py
+++ b/erpnext/tests/test_init.py
@@ -1,9 +1,8 @@
-from __future__ import unicode_literals
import unittest
import frappe
+
from erpnext import encode_company_abbr
-from six.moves import range
test_records = frappe.get_test_records('Company')
diff --git a/erpnext/tests/test_notifications.py b/erpnext/tests/test_notifications.py
index 1fd90be..669bf6f 100644
--- a/erpnext/tests/test_notifications.py
+++ b/erpnext/tests/test_notifications.py
@@ -1,12 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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 5b3f45a..10d62ce 100644
--- a/erpnext/tests/test_regional.py
+++ b/erpnext/tests/test_regional.py
@@ -1,5 +1,9 @@
-from __future__ import unicode_literals
-import unittest, frappe, erpnext
+import unittest
+
+import frappe
+
+import erpnext
+
@erpnext.allow_regional
def test_method():
diff --git a/erpnext/tests/test_search.py b/erpnext/tests/test_search.py
index f60e5e4..c169458 100644
--- a/erpnext/tests/test_search.py
+++ b/erpnext/tests/test_search.py
@@ -1,8 +1,9 @@
-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 f55137b..fccfd0d 100644
--- a/erpnext/tests/test_subcontracting.py
+++ b/erpnext/tests/test_subcontracting.py
@@ -1,16 +1,22 @@
-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):
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..4a451ab 100644
--- a/erpnext/tests/test_woocommerce.py
+++ b/erpnext/tests/test_woocommerce.py
@@ -1,15 +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'):
- company = frappe.new_doc("Company")
- company.company_name = "Woocommerce"
- company.abbr = "W"
- company.default_currency = "INR"
- company.save()
woo_settings = frappe.get_doc("Woocommerce Settings")
if not woo_settings.secret:
@@ -18,14 +18,14 @@
woo_settings.api_consumer_key = "ck_fd43ff5756a6abafd95fadb6677100ce95a758a1"
woo_settings.api_consumer_secret = "cs_94360a1ad7bef7fa420a40cf284f7b3e0788454e"
woo_settings.enable_sync = 1
- woo_settings.company = "Woocommerce"
- woo_settings.tax_account = "Sales Expenses - W"
- woo_settings.f_n_f_account = "Expenses - W"
+ woo_settings.company = "_Test Company"
+ woo_settings.tax_account = "Sales Expenses - _TC"
+ woo_settings.f_n_f_account = "Expenses - _TC"
woo_settings.creation_user = "Administrator"
woo_settings.save(ignore_permissions=True)
def test_sales_order_for_woocommerce(self):
- frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]}
+ frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"_Test Company","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]}
order()
self.assertTrue(frappe.get_value("Customer",{"woocommerce_email":"tony@gmail.com"}))
diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py
index 902fd64..9c8c371 100644
--- a/erpnext/tests/ui_test_helpers.py
+++ b/erpnext/tests/ui_test_helpers.py
@@ -1,11 +1,14 @@
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)
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index 11eb6af..fbf2594 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -2,9 +2,32 @@
# License: GNU General Public License v3. See license.txt
import copy
+import signal
+import unittest
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)
+
+
+class ERPNextTestCase(unittest.TestCase):
+ """A sane default test class for ERPNext tests."""
+
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ frappe.db.commit()
+ return super().setUpClass()
+
+ @classmethod
+ def tearDownClass(cls) -> None:
+ frappe.db.rollback()
+ return super().tearDownClass()
+
def create_test_contact_and_address():
frappe.db.sql('delete from tabContact')
@@ -77,3 +100,59 @@
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
+
+
+def timeout(seconds=30, error_message="Test timed out."):
+ """ Timeout decorator to ensure a test doesn't run for too long.
+
+ adapted from https://stackoverflow.com/a/2282656"""
+ def decorator(func):
+ def _handle_timeout(signum, frame):
+ raise Exception(error_message)
+
+ def wrapper(*args, **kwargs):
+ signal.signal(signal.SIGALRM, _handle_timeout)
+ signal.alarm(seconds)
+ try:
+ result = func(*args, **kwargs)
+ finally:
+ signal.alarm(0)
+ return result
+ return wrapper
+ return decorator
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index ca03a78..d46ffb5 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -1847,7 +1847,7 @@
Overlap in scoring between {0} and {1},Überlappung beim Scoring zwischen {0} und {1},
Overlapping conditions found between:,Überlagernde Bedingungen gefunden zwischen:,
Owner,Besitzer,
-PAN,PFANNE,
+PAN,PAN,
POS,Verkaufsstelle,
POS Profile,Verkaufsstellen-Profil,
POS Profile is required to use Point-of-Sale,"POS-Profil ist erforderlich, um Point-of-Sale zu verwenden",
diff --git a/erpnext/utilities/__init__.py b/erpnext/utilities/__init__.py
index 0a5aa3c..3749cde 100644
--- a/erpnext/utilities/__init__.py
+++ b/erpnext/utilities/__init__.py
@@ -1,9 +1,11 @@
## 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 0f9f2f8..faf3fd4 100644
--- a/erpnext/utilities/activation.py
+++ b/erpnext/utilities/activation.py
@@ -1,11 +1,12 @@
# 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, erpnext
+import frappe
from frappe import _
-from six import iteritems
+
+import erpnext
+
def get_level():
activation_level = 0
@@ -43,7 +44,7 @@
"Work Order": 5
}
- for doctype, min_count in iteritems(doctypes):
+ for doctype, min_count in doctypes.items():
count = frappe.db.count(doctype)
if count > min_count:
activation_level += 1
diff --git a/erpnext/utilities/bot.py b/erpnext/utilities/bot.py
index 485b0b3..87a3508 100644
--- a/erpnext/utilities/bot.py
+++ b/erpnext/utilities/bot.py
@@ -1,12 +1,11 @@
# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-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):
diff --git a/erpnext/utilities/doctype/__init__.py b/erpnext/utilities/doctype/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/utilities/doctype/__init__.py
+++ b/erpnext/utilities/doctype/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.py b/erpnext/utilities/doctype/rename_tool/rename_tool.py
index 5e3ac1a..74de54a 100644
--- a/erpnext/utilities/doctype/rename_tool/rename_tool.py
+++ b/erpnext/utilities/doctype/rename_tool/rename_tool.py
@@ -3,12 +3,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
diff --git a/erpnext/utilities/doctype/sms_log/__init__.py b/erpnext/utilities/doctype/sms_log/__init__.py
index baffc48..e69de29 100644
--- a/erpnext/utilities/doctype/sms_log/__init__.py
+++ b/erpnext/utilities/doctype/sms_log/__init__.py
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/utilities/doctype/sms_log/sms_log.py b/erpnext/utilities/doctype/sms_log/sms_log.py
index e634f80..85140f3 100644
--- a/erpnext/utilities/doctype/sms_log/sms_log.py
+++ b/erpnext/utilities/doctype/sms_log/sms_log.py
@@ -1,10 +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 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..5f7abdc 100644
--- a/erpnext/utilities/doctype/sms_log/test_sms_log.py
+++ b/erpnext/utilities/doctype/sms_log/test_sms_log.py
@@ -1,9 +1,6 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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..dc50053 100644
--- a/erpnext/utilities/doctype/video/test_video.py
+++ b/erpnext/utilities/doctype/video/test_video.py
@@ -1,10 +1,9 @@
-# -*- 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 TestVideo(unittest.TestCase):
pass
diff --git a/erpnext/utilities/doctype/video/video.py b/erpnext/utilities/doctype/video/video.py
index c2e414e..ae13952 100644
--- a/erpnext/utilities/doctype/video/video.py
+++ b/erpnext/utilities/doctype/video/video.py
@@ -1,17 +1,17 @@
-# -*- 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 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
+
class Video(Document):
def validate(self):
if self.provider == "YouTube" and is_tracking_enabled():
@@ -86,7 +86,7 @@
Returns video id from url
:param youtube url: String URL
"""
- if not isinstance(url, string_types):
+ if not isinstance(url, str):
frappe.throw(_("URL can only be a string"), title=_("Invalid URL"))
pattern = re.compile(r'[a-z\:\//\.]+(youtube|youtu)\.(com|be)/(watch\?v=|embed/|.+\?v=)?([^"&?\s]{11})?')
diff --git a/erpnext/utilities/doctype/video_settings/test_video_settings.py b/erpnext/utilities/doctype/video_settings/test_video_settings.py
index b217afe..25cac6c 100644
--- a/erpnext/utilities/doctype/video_settings/test_video_settings.py
+++ b/erpnext/utilities/doctype/video_settings/test_video_settings.py
@@ -1,10 +1,9 @@
-# -*- 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 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 db021b4..6f1e2bb 100644
--- a/erpnext/utilities/doctype/video_settings/video_settings.py
+++ b/erpnext/utilities/doctype/video_settings/video_settings.py
@@ -1,12 +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 apiclient.discovery import build
from frappe import _
from frappe.model.document import Document
-from apiclient.discovery import build
+
class VideoSettings(Document):
def validate(self):
diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py
index 384d841..c18ce10 100644
--- a/erpnext/utilities/hierarchy_chart.py
+++ b/erpnext/utilities/hierarchy_chart.py
@@ -1,22 +1,27 @@
# 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(parent, parent_name, method, company):
+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)
- data = method(parent, company)
- result = [dict(parent=parent, parent_name=parent_name, data=data)]
+ root_nodes = method(company=company)
+ result = []
+ nodes_to_expand = []
- nodes_to_expand = [{'id': d.get('id'), 'name': d.get('name')} for d in data if d.get('expandable')]
+ 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)
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index 70b4176..e9e4baa 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -1,13 +1,14 @@
# 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 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 +128,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 29a489d..a185a70 100644
--- a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
@@ -1,11 +1,12 @@
# 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 flt
+
def execute(filters=None):
if not frappe.db.get_single_value("Video Settings", "enable_youtube_tracking") or not filters:
return [], []
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index db99726..1d8b3a8 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -1,14 +1,14 @@
# 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 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 erpnext.controllers.status_updater import StatusUpdater
-from six import string_types
class UOMMustBeIntegerError(frappe.ValidationError): pass
@@ -162,6 +162,28 @@
return ret
+ def reset_default_field_value(self, default_field: str, child_table: str, child_table_field: str):
+ """ Reset "Set default X" fields on forms to avoid confusion.
+
+ example:
+ doc = {
+ "set_from_warehouse": "Warehouse A",
+ "items": [{"from_warehouse": "warehouse B"}, {"from_warehouse": "warehouse A"}],
+ }
+ Since this has dissimilar values in child table, the default field will be erased.
+
+ doc.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
+ """
+ child_table_values = set()
+
+ for row in self.get(child_table):
+ child_table_values.add(row.get(child_table_field))
+
+ if len(child_table_values) > 1:
+ self.set(default_field, None)
+ else:
+ self.set(default_field, list(child_table_values)[0])
+
def delete_events(ref_type, ref_name):
events = frappe.db.sql_list(""" SELECT
distinct `tabEvent`.name
@@ -177,7 +199,7 @@
frappe.delete_doc("Event", events, for_reload=True)
def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None):
- if isinstance(qty_fields, string_types):
+ if isinstance(qty_fields, str):
qty_fields = [qty_fields]
distinct_uoms = list(set(d.get(uom_field) for d in doc.get_all_children()))
diff --git a/erpnext/utilities/web_form/addresses/addresses.py b/erpnext/utilities/web_form/addresses/addresses.py
index 3fd1017..db32552 100644
--- a/erpnext/utilities/web_form/addresses/addresses.py
+++ b/erpnext/utilities/web_form/addresses/addresses.py
@@ -1,7 +1,3 @@
-from __future__ import unicode_literals
-
-import frappe
-
def get_context(context):
# do your magic here
context.show_sidebar = True
diff --git a/erpnext/utilities/workspace/utilities/utilities.json b/erpnext/utilities/workspace/utilities/utilities.json
index 4ad4afb..02a8af5 100644
--- a/erpnext/utilities/workspace/utilities/utilities.json
+++ b/erpnext/utilities/workspace/utilities/utilities.json
@@ -1,19 +1,12 @@
{
- "category": "",
"charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Video\", \"col\": 4}}]",
"creation": "2020-09-10 12:21:22.335307",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
"for_user": "",
"hide_custom": 0,
"idx": 0,
- "is_default": 0,
- "is_standard": 0,
"label": "Utilities",
"links": [
{
@@ -47,15 +40,12 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:16:03.350804",
+ "modified": "2021-08-05 12:16:03.350805",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Utilities",
- "onboarding": "",
"owner": "user@erpnext.com",
"parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html
index 7c18ecc..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 %}
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/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.py b/erpnext/www/book_appointment/verify/index.py
index bd766c0..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():
diff --git a/erpnext/www/lms/content.py b/erpnext/www/lms/content.py
index 05cbb16..b187a78 100644
--- a/erpnext/www/lms/content.py
+++ b/erpnext/www/lms/content.py
@@ -1,7 +1,7 @@
-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/course.py b/erpnext/www/lms/course.py
index c18d64e..012e25c 100644
--- a/erpnext/www/lms/course.py
+++ b/erpnext/www/lms/course.py
@@ -1,7 +1,7 @@
-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 c14b943..035f7d9 100644
--- a/erpnext/www/lms/index.py
+++ b/erpnext/www/lms/index.py
@@ -1,7 +1,7 @@
-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/profile.py b/erpnext/www/lms/profile.py
index 7e338e3..8cd2f24 100644
--- a/erpnext/www/lms/profile.py
+++ b/erpnext/www/lms/profile.py
@@ -1,7 +1,7 @@
-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/program.py b/erpnext/www/lms/program.py
index a4f588c..db2653a 100644
--- a/erpnext/www/lms/program.py
+++ b/erpnext/www/lms/program.py
@@ -1,8 +1,8 @@
-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):
diff --git a/erpnext/www/lms/topic.py b/erpnext/www/lms/topic.py
index 9938280..17fc8f7 100644
--- a/erpnext/www/lms/topic.py
+++ b/erpnext/www/lms/topic.py
@@ -1,7 +1,7 @@
-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/payment_setup_certification.py b/erpnext/www/payment_setup_certification.py
index 6b02e4e..c65cddb 100644
--- a/erpnext/www/payment_setup_certification.py
+++ b/erpnext/www/payment_setup_certification.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
import frappe
no_cache = 1
diff --git a/erpnext/www/support/index.py b/erpnext/www/support/index.py
index 70090c7..408ddf4 100644
--- a/erpnext/www/support/index.py
+++ b/erpnext/www/support/index.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
import frappe
+
def get_context(context):
context.no_cache = 1
context.align_greeting = ''
diff --git a/package.json b/package.json
index 5bc1e56..6c11e9d 100644
--- a/package.json
+++ b/package.json
@@ -11,16 +11,9 @@
"bugs": {
"url": "https://github.com/frappe/erpnext/issues"
},
- "devDependencies": {
- "snyk": "^1.518.0"
- },
+ "devDependencies": {},
"dependencies": {
- "onscan.js": "^1.5.2",
- "html2canvas": "^1.1.4"
- },
- "scripts": {
- "snyk-protect": "snyk protect",
- "prepare": "yarn run snyk-protect"
- },
- "snyk": true
+ "html2canvas": "^1.1.4",
+ "onscan.js": "^1.5.2"
+ }
}
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/requirements.txt b/requirements.txt
index f28906a..faefb77 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,3 @@
-future==0.18.2
# frappe # https://github.com/frappe/frappe is installed during bench-init
gocardless-pro~=1.22.0
googlemaps # used in ERPNext, but dependency is defined in Frappe
diff --git a/setup.py b/setup.py
index a864e73..8140700 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
from setuptools import setup, find_packages
import re, ast
diff --git a/yarn.lock b/yarn.lock
index 82e9821..8e5d1bd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,1006 +2,11 @@
# 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-arraybuffer@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45"
integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==
-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==
-
css-line-break@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef"
@@ -1009,481 +14,6 @@
dependencies:
base64-arraybuffer "^0.2.0"
-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:
- 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"
-
-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:
- 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==
- dependencies:
- function-bind "^1.1.1"
- has "^1.0.3"
- has-symbols "^1.0.1"
-
-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==
- 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"
-
html2canvas@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.1.4.tgz#53ae91cd26e9e9e623c56533cccb2e3f57c8124c"
@@ -1491,2086 +21,7 @@
dependencies:
css-line-break "1.1.1"
-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"
-
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==