Compare commits

..

18 Commits

Author SHA1 Message Date
Anurag Mishra
7539cfecd2 Merge branch 'overtime-feature' of https://github.com/anurag810/erpnext into overtime-feature 2021-08-11 15:18:08 +05:30
Anurag Mishra
18f04111d6 fix: Code optimisation 2021-08-11 15:17:42 +05:30
Anurag Mishra
b2ba9aacb2 Merge branch 'version-13-hotfix' into overtime-feature 2021-08-11 13:32:52 +05:30
Anurag Mishra
4a6694e80a fix: changes requested 2021-08-11 12:59:22 +05:30
Anurag Mishra
8db493b2be chore: formatting 2021-08-10 12:45:51 +05:30
Anurag Mishra
21bc752da5 fix: Removed print statement 2021-08-10 12:45:51 +05:30
Anurag Mishra
96db78f7bc fix: Test Cases 2021-08-10 12:45:51 +05:30
Anurag Mishra
32f3b24c3a fix: Test Cases 2021-08-10 12:45:51 +05:30
Anurag Mishra
10647bfcd7 fix: test 2021-08-10 12:45:51 +05:30
Anurag Mishra
7d272dc648 fix: test Payroll Entry 2021-08-10 12:45:51 +05:30
Anurag Mishra
b96165883e fix: fixed test and resolved conflicts 2021-08-10 12:45:51 +05:30
Anurag Mishra
5683857f9f fix: sider 2021-08-10 12:45:50 +05:30
Anurag Mishra
73b3121127 fix: some enhancemets and test cases 2021-08-10 12:45:50 +05:30
Anurag Mishra
769d774ccc feat: Validation, sider and form dashbord 2021-08-10 12:45:50 +05:30
Anurag Mishra
da2e95dbcc feat: Overtime based on Attendance and Timesheet 2021-08-10 12:44:50 +05:30
Anurag Mishra
3851d15360 feat:overtime 2021-08-10 12:41:45 +05:30
Anurag Mishra
e4fd6d7763 feat: Overtime 2021-08-10 12:41:45 +05:30
Anurag Mishra
8f1850b21c feat: settings for Overtime 2021-08-10 12:38:33 +05:30
3633 changed files with 36933 additions and 59725 deletions

View File

@@ -28,7 +28,6 @@ ignore =
B007,
B950,
W191,
E124, # closing bracket, irritating while writing QB code
max-line-length = 200
exclude=.github/helper/semgrep_rules

View File

@@ -10,10 +10,3 @@
# This commit just changes spaces to tabs for indentation in some files
5f473611bd6ed57703716244a054d3fb5ba9cd23
# Whitespace trimming throughout codebase
9bb69e711a5da43aaf8c8ecb5601aeffd89dbe5a
f0bcb753fb7ebbb64bb0d6906d431d002f0f7d8f
# imports cleanup
4b2be2999f2203493b49bf74c5b440d49e38b5e3

View File

@@ -1,72 +0,0 @@
[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

View File

@@ -1,7 +1,7 @@
import sys
import requests
from urllib.parse import urlparse
import requests
docs_repos = [
"frappe_docs",
@@ -24,8 +24,6 @@ def docs_link_exists(body):
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__":
@@ -34,15 +32,11 @@ if __name__ == "__main__":
if response.ok:
payload = response.json()
title = (payload.get("title") or "").lower().strip()
head_sha = (payload.get("head") or {}).get("sha")
body = (payload.get("body") or "").lower()
title = payload.get("title", "").lower()
head_sha = payload.get("head", {}).get("sha")
body = payload.get("body", "").lower()
if (title.startswith("feat")
and head_sha
and "no-docs" not in body
and "backport" not in body
):
if title.startswith("feat") and head_sha and "no-docs" not in body:
if docs_link_exists(body):
print("Documentation Link Found. You're Awesome! 🎉")

View File

@@ -4,7 +4,11 @@ set -e
cd ~ || exit
sudo apt-get install redis-server libcups2-dev
sudo apt-get install redis-server
sudo apt install nodejs
sudo apt install npm
pip install frappe-bench
@@ -28,6 +32,7 @@ wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/w
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

38
.github/helper/semgrep_rules/README.md vendored Normal file
View File

@@ -0,0 +1,38 @@
# 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

View File

@@ -1,5 +1,6 @@
import frappe
from frappe import _
from frappe import _, flt
from frappe.model.document import Document

View File

@@ -131,21 +131,3 @@ rules:
key `$X` is uselessly assigned twice. This could be a potential bug.
languages: [python]
severity: ERROR
- id: frappe-manual-commit
patterns:
- pattern: frappe.db.commit()
- pattern-not-inside: |
try:
...
except ...:
...
message: |
Manually commiting a transaction is highly discouraged. Read about the transaction model implemented by Frappe Framework before adding manual commits: https://frappeframework.com/docs/user/en/api/database#database-transaction-model If you think manual commit is required then add a comment explaining why and `// nosemgrep` on the same line.
paths:
exclude:
- "**/patches/**"
- "**/demo/**"
languages: [python]
severity: ERROR

View File

@@ -1,14 +0,0 @@
from frappe import _
# ruleid: frappe-missing-translate-function-in-report-python
{"label": "Field Label"}
# ruleid: frappe-missing-translate-function-in-report-python
dict(label="Field Label")
# ok: frappe-missing-translate-function-in-report-python
{"label": _("Field Label")}
# ok: frappe-missing-translate-function-in-report-python
dict(label=_("Field Label"))

View File

@@ -0,0 +1,6 @@
def function_name(input):
# ruleid: frappe-codeinjection-eval
eval(input)
# ok: frappe-codeinjection-eval
eval("1 + 1")

View File

@@ -0,0 +1,10 @@
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

View File

@@ -0,0 +1,44 @@
// 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])

View File

@@ -0,0 +1,61 @@
# 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

View File

@@ -0,0 +1,64 @@
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

9
.github/helper/semgrep_rules/ux.js vendored Normal file
View File

@@ -0,0 +1,9 @@
// 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") }}. ');

View File

@@ -1,5 +1,6 @@
import frappe
from frappe import _, msgprint, throw
from frappe import msgprint, throw, _
# ruleid: frappe-missing-translate-function-python
throw("Error Occured")

30
.github/helper/semgrep_rules/ux.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
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

View File

@@ -8,7 +8,6 @@ on:
jobs:
main:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout Actions
uses: actions/checkout@v2

View File

@@ -11,4 +11,4 @@ jobs:
- name: curl
run: |
apk add curl bash
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"}'
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

View File

@@ -6,7 +6,6 @@ on:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 'Setup Environment'

View File

@@ -1,31 +0,0 @@
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

View File

@@ -1,22 +1,10 @@
name: Patch
on:
pull_request:
paths-ignore:
- '**.js'
- '**.md'
types: [opened, unlabeled, synchronize, reopened]
workflow_dispatch:
concurrency:
group: patch-mariadb-v13-${{ github.event.number }}
cancel-in-progress: true
on: [pull_request, workflow_dispatch]
jobs:
test:
runs-on: ubuntu-18.04
timeout-minutes: 60
name: Patch Test
@@ -30,24 +18,13 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Check for merge conficts label
if: ${{ contains(github.event.pull_request.labels.*.name, 'conflicts') }}
run: |
echo "Remove merge conflicts and remove conflict label to run CI"
exit 1
- name: Clone
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 12
check-latest: true
python-version: 3.6
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts

18
.github/workflows/semgrep.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
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

View File

@@ -1,26 +1,10 @@
name: Server
on:
pull_request:
paths-ignore:
- '**.js'
- '**.md'
types: [opened, unlabeled, synchronize, reopened]
workflow_dispatch:
push:
branches: [ develop ]
paths-ignore:
- '**.js'
- '**.md'
concurrency:
group: server-mariadb-v13-${{ github.event.number }}
cancel-in-progress: true
on: [pull_request, workflow_dispatch]
jobs:
test:
runs-on: ubuntu-18.04
timeout-minutes: 60
strategy:
fail-fast: false
@@ -40,12 +24,6 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Check for merge conficts label
if: ${{ contains(github.event.pull_request.labels.*.name, 'conflicts') }}
run: |
echo "Remove merge conflicts and remove conflict label to run CI"
exit 1
- name: Clone
uses: actions/checkout@v2
@@ -54,12 +32,6 @@ jobs:
with:
python-version: 3.7
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 12
check-latest: true
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
@@ -100,8 +72,39 @@ jobs:
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
- name: Run Tests
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage
env:
TYPE: server
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_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 }}

View File

@@ -2,18 +2,11 @@ name: UI
on:
pull_request:
paths-ignore:
- '**.md'
workflow_dispatch:
concurrency:
group: ui-v13-${{ github.event.number }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-18.04
timeout-minutes: 60
strategy:
fail-fast: false
@@ -99,13 +92,11 @@ jobs:
run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests
- name: cypress pre-requisites
run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile
run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 --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

View File

@@ -1,58 +0,0 @@
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 }}"

View File

@@ -1,38 +0,0 @@
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

8
.snyk Normal file
View File

@@ -0,0 +1,8 @@
# 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'

View File

@@ -2,12 +2,7 @@ context('Organizational Chart', () => {
before(() => {
cy.login();
cy.visit('/app/website');
});
it('navigates to org chart', () => {
cy.visit('/app');
cy.visit('/app/organizational-chart');
cy.url().should('include', '/organizational-chart');
cy.awesomebar('Organizational Chart');
cy.window().its('frappe.csrf_token').then(csrf_token => {
return cy.request({
@@ -24,7 +19,7 @@ context('Organizational Chart', () => {
cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
cy.get('@input')
.clear({ force: true })
.type('Test Org Chart{downarrow}{enter}', { force: true })
.type('Test Org Chart{enter}', { force: true })
.blur({ force: true });
});
});

View File

@@ -1,14 +1,9 @@
context('Organizational Chart Mobile', () => {
before(() => {
cy.login();
cy.visit('/app/website');
});
it('navigates to org chart', () => {
cy.viewport(375, 667);
cy.visit('/app');
cy.visit('/app/organizational-chart');
cy.url().should('include', '/organizational-chart');
cy.visit('/app/website');
cy.awesomebar('Organizational Chart');
cy.window().its('frappe.csrf_token').then(csrf_token => {
return cy.request({
@@ -25,7 +20,7 @@ context('Organizational Chart Mobile', () => {
cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
cy.get('@input')
.clear({ force: true })
.type('Test Org Chart{downarrow}{enter}', { force: true })
.type('Test Org Chart{enter}', { force: true })
.blur({ force: true });
});
});

View File

@@ -6,4 +6,4 @@
"scss/at-rule-no-unknown": true,
"no-descending-specificity": null
}
}
}

View File

@@ -1,11 +1,11 @@
# -*- 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.22.0'
__version__ = '13.2.0'
def get_default_company(user=None):
'''Get default company for user'''
@@ -56,9 +56,9 @@ def set_perpetual_inventory(enable=1, company=None):
company.enable_perpetual_inventory = enable
company.save()
def encode_company_abbr(name, company=None, abbr=None):
def encode_company_abbr(name, company):
'''Returns name encoded with company abbreviation'''
company_abbr = abbr or frappe.get_cached_value('Company', company, "abbr")
company_abbr = frappe.get_cached_value('Company', company, "abbr")
parts = name.rsplit(" - ", 1)
if parts[-1].lower() != company_abbr.lower():

View File

@@ -1,11 +1,7 @@
import frappe
from frappe import _
from frappe.contacts.doctype.address.address import (
Address,
get_address_display,
get_address_templates,
)
from frappe.contacts.doctype.address.address import Address
from frappe.contacts.doctype.address.address import get_address_templates
class ERPNextAddress(Address):
def validate(self):
@@ -26,19 +22,6 @@ class ERPNextAddress(Address):
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 = [

View File

@@ -19,4 +19,4 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = {
reqd: 1
},
]
};
};

View File

@@ -1,15 +1,15 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from __future__ import unicode_literals
import frappe, json
from frappe import _
from frappe.utils import add_to_date, formatdate, get_link_to_form, getdate, nowdate
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.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,

View File

@@ -1,26 +1,12 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.email import sendmail_to_system_managers
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 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
def validate_service_stop_date(doc):
''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
@@ -121,7 +107,6 @@ def get_booking_dates(doc, item, posting_date=None):
prev_gl_entry = frappe.db.sql('''
select name, posting_date from `tabGL Entry` where company=%s and account=%s and
voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0
order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
@@ -229,7 +214,6 @@ def get_already_booked_amount(doc, item):
gl_entries_details = frappe.db.sql('''
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
@@ -257,13 +241,11 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
accounts_frozen_upto = frappe.get_cached_value('Accounts Settings', 'None', 'acc_frozen_upto')
def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return
account_currency = get_account_currency(item.expense_account or item.income_account)
account_currency = get_account_currency(item.expense_account)
if doc.doctype == "Sales Invoice":
against, project = doc.customer, doc.project
credit_account, debit_account = item.income_account, item.deferred_revenue_account
@@ -284,10 +266,6 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
if not amount:
return
# check if books nor frozen till endate:
if accounts_frozen_upto and (end_date) <= getdate(accounts_frozen_upto):
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
if via_journal_entry:
book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
@@ -381,16 +359,12 @@ def make_gl_entries(doc, credit_account, debit_account, against,
try:
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
frappe.db.commit()
except Exception as e:
if frappe.flags.in_test:
traceback = frappe.get_traceback()
frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
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
except:
frappe.db.rollback()
traceback = frappe.get_traceback()
frappe.log_error(message=traceback)
frappe.flags.deferred_accounting_error = True
def send_mail(deferred_process):
title = _("Error while processing deferred accounting for {0}").format(deferred_process)
@@ -415,6 +389,8 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
'account': credit_account,
'credit': base_amount,
'credit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
@@ -427,6 +403,8 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
'account': debit_account,
'debit': base_amount,
'debit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
@@ -452,12 +430,10 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
if submit:
journal_entry.submit()
frappe.db.commit()
except Exception:
except:
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.log_error(message=traceback)
frappe.flags.deferred_accounting_error = True
@@ -474,3 +450,5 @@ def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
return debit_account
else:
return credit_account

View File

@@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@@ -43,12 +43,12 @@ frappe.ui.form.on('Account', {
frm.trigger('add_toolbar_buttons');
}
if (frm.has_perm('write')) {
frm.add_custom_button(__('Merge Account'), function () {
frm.trigger("merge_account");
}, __('Actions'));
frm.add_custom_button(__('Update Account Name / Number'), function () {
frm.trigger("update_account_number");
}, __('Actions'));
});
frm.add_custom_button(__('Merge Account'), function () {
frm.trigger("merge_account");
});
}
}
},
@@ -59,12 +59,11 @@ frappe.ui.form.on('Account', {
}
},
add_toolbar_buttons: function(frm) {
frm.add_custom_button(__('Chart of Accounts'), () => {
frappe.set_route("Tree", "Account");
}, __('View'));
frm.add_custom_button(__('Chart of Accounts'),
function () { frappe.set_route("Tree", "Account"); });
if (frm.doc.is_group == 1) {
frm.add_custom_button(__('Convert to Non-Group'), function () {
frm.add_custom_button(__('Group to Non-Group'), function () {
return frappe.call({
doc: frm.doc,
method: 'convert_group_to_ledger',
@@ -72,11 +71,10 @@ frappe.ui.form.on('Account', {
frm.refresh();
}
});
}, __('Actions'));
});
} else if (cint(frm.doc.is_group) == 0
&& frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
frm.add_custom_button(__('General Ledger'), function () {
cur_frm.add_custom_button(__('Ledger'), function () {
frappe.route_options = {
"account": frm.doc.name,
"from_date": frappe.sys_defaults.year_start_date,
@@ -84,9 +82,9 @@ frappe.ui.form.on('Account', {
"company": frm.doc.company
};
frappe.set_route("query-report", "General Ledger");
}, __('View'));
});
frm.add_custom_button(__('Convert to Group'), function () {
frm.add_custom_button(__('Non-Group to Group'), function () {
return frappe.call({
doc: frm.doc,
method: 'convert_ledger_to_group',
@@ -94,7 +92,7 @@ frappe.ui.form.on('Account', {
frm.refresh();
}
});
}, __('Actions'));
});
}
},

View File

@@ -1,15 +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, 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
@@ -197,7 +194,7 @@ class Account(NestedSet):
"company": company,
# parent account's currency should be passed down to child account's curreny
# if it is None, it picks it up from default company currency, which might be unintended
"account_currency": erpnext.get_company_currency(company),
"account_currency": self.account_currency,
"parent_account": parent_acc_name_map[company]
})
@@ -208,7 +205,8 @@ class Account(NestedSet):
# update the parent company's value in child companies
doc = frappe.get_doc("Account", child_account)
parent_value_changed = False
for field in ['account_type', 'freeze_account', 'balance_must_be']:
for field in ['account_type', 'account_currency',
'freeze_account', 'balance_must_be']:
if doc.get(field) != self.get(field):
parent_value_changed = True
doc.set(field, self.get(field))

View File

@@ -45,50 +45,6 @@ frappe.treeview_settings["Account"] = {
],
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:[
{
@@ -166,6 +122,24 @@ frappe.treeview_settings["Account"] = {
}
}, "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"),
@@ -176,7 +150,7 @@ frappe.treeview_settings["Account"] = {
&& node.expandable && !node.hide_add;
},
click: function() {
var me = frappe.views.trees['Account'];
var me = frappe.treeview_settings['Account'].treeview;
me.new_node();
},
btnClass: "hidden-xs"

View File

@@ -1,17 +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 os
import frappe
import frappe, os, json
from frappe.utils import cstr
from frappe.utils.nestedset import rebuild_tree
from six import iteritems
from unidecode import unidecode
from six import iteritems
from frappe.utils.nestedset import rebuild_tree
def create_charts(company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None):
def create_charts(company, chart_template=None, existing_company=None, custom_chart=None):
chart = custom_chart or get_chart(chart_template, existing_company)
if chart:
accounts = []
@@ -21,7 +19,7 @@ def create_charts(company, chart_template=None, existing_company=None, custom_ch
if root_account:
root_type = child.get("root_type")
if account_name not in ["account_name", "account_number", "account_type",
if account_name not in ["account_number", "account_type",
"root_type", "is_group", "tax_rate"]:
account_number = cstr(child.get("account_number")).strip()
@@ -34,7 +32,7 @@ def create_charts(company, chart_template=None, existing_company=None, custom_ch
account = frappe.get_doc({
"doctype": "Account",
"account_name": child.get('account_name') if from_coa_importer else account_name,
"account_name": account_name,
"company": company,
"parent_account": parent,
"is_group": is_group,
@@ -80,7 +78,7 @@ def add_suffix_if_duplicate(account_name, account_number, accounts):
def identify_is_group(child):
if child.get("is_group"):
is_group = child.get("is_group")
elif len(set(child.keys()) - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])):
elif len(set(child.keys()) - set(["account_type", "root_type", "is_group", "tax_rate", "account_number"])):
is_group = 1
else:
is_group = 0
@@ -93,14 +91,11 @@ def get_chart(chart_template, existing_company=None):
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",)
@@ -212,7 +207,7 @@ def validate_bank_account(coa, bank_account):
return (bank_account in accounts)
@frappe.whitelist()
def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
def build_tree_from_json(chart_template, chart_data=None):
''' get chart template from its folder and parse the json to be rendered as tree '''
chart = chart_data or get_chart(chart_template)
@@ -225,12 +220,9 @@ def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=Fals
''' recursively called to form a parent-child based list of dict from chart template '''
for account_name, child in iteritems(children):
account = {}
if account_name in ["account_name", "account_number", "account_type",\
if account_name in ["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) \

View File

@@ -4,14 +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
import frappe
from frappe.utils.csvutils import read_csv_content
import frappe
from six import iteritems
path = "/Users/nabinhait/projects/odoo/addons"

View File

@@ -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
from frappe import _
def get():
return {
_("Application of Funds (Assets)"): {

View File

@@ -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
from frappe import _
def get():
return {
_("Application of Funds (Assets)"): {

View File

@@ -1,14 +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 erpnext.accounts.doctype.account.account import merge_account, update_account_number
from erpnext.stock import get_company_default_inventory_account, get_warehouse_account
from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
from erpnext.accounts.doctype.account.account import update_account_number, merge_account
class TestAccount(unittest.TestCase):
def test_rename_account(self):

View File

@@ -60,4 +60,4 @@ frappe.ui.form.on('Accounting Dimension Detail', {
let row = locals[cdt][cdn];
row.reference_document = frm.doc.document_type;
}
});
});

View File

@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
from __future__ import unicode_literals
import frappe
from frappe import _, scrub
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.model import core_doctypes_list
from frappe import _
import json
from frappe.model.document import Document
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
class AccountingDimension(Document):
def before_insert(self):
@@ -46,9 +47,9 @@ class AccountingDimension(Document):
def on_trash(self):
if frappe.flags.in_test:
delete_accounting_dimension(doc=self)
delete_accounting_dimension(doc=self, queue='long')
else:
frappe.enqueue(delete_accounting_dimension, doc=self, queue='long')
frappe.enqueue(delete_accounting_dimension, doc=self)
def set_fieldname_and_label(self):
if not self.label:

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
from __future__ import unicode_literals
import frappe
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
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
test_dependencies = ['Cost Center', 'Location', 'Warehouse', 'Department']
@@ -113,3 +113,5 @@ def disable_dimension():
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
dimension2.disabled = 1
dimension2.save()

View File

@@ -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

View File

@@ -8,7 +8,7 @@ frappe.ui.form.on('Accounting Dimension Filter', {
}
let help_content =
`<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
`<table class="table table-bordered" style="background-color: #f9f9f9;">
<tr><td>
<p>
<i class="fa fa-hand-right"></i>
@@ -79,4 +79,4 @@ frappe.ui.form.on('Allowed Dimension', {
row.accounting_dimension = frm.doc.accounting_dimension;
frm.refresh_field("dimensions");
}
});
});

View File

@@ -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()

View File

@@ -1,15 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
from __future__ import unicode_literals
import frappe
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
create_dimension,
disable_dimension,
)
import unittest
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']

View File

@@ -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 import _
from frappe.model.document import Document
from frappe import _
class OverlapError(frappe.ValidationError): pass
@@ -56,4 +56,4 @@ class AccountingPeriod(Document):
self.append('closed_documents', {
"document_type": doctype_for_closing.document_type,
"closed": doctype_for_closing.closed
})
})

View File

@@ -0,0 +1,23 @@
/* 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()
]);
});

View File

@@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
from __future__ import unicode_literals
import frappe
from frappe.utils import add_months, nowdate
import unittest
from frappe.utils import nowdate, add_months
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
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']

View File

@@ -48,4 +48,4 @@ frappe.tour['Accounts Settings'] = [
title: "Unlink Advance Payment on Cancellation of Order",
description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.")
}
];
];

View File

@@ -18,7 +18,6 @@
"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",
@@ -175,7 +174,7 @@
"default": "0",
"fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check",
"label": "Automatically Fetch Payment Terms from Order"
"label": "Automatically Fetch Payment Terms"
},
{
"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 ",
@@ -270,12 +269,6 @@
"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",
@@ -283,7 +276,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2021-10-11 17:42:36.427699",
"modified": "2021-07-12 18:54:29.084958",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -3,14 +3,12 @@
# 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 erpnext.stock.utils import check_pending_reposting
from frappe.model.document import Document
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
class AccountsSettings(Document):
@@ -21,13 +19,9 @@ class AccountsSettings(Document):
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:
@@ -43,7 +37,7 @@ class AccountsSettings(Document):
def toggle_discount_accounting_fields(self):
enable_discount_accounting = cint(self.enable_discount_accounting)
for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
if enable_discount_accounting:
@@ -58,9 +52,4 @@ class AccountsSettings(Document):
else:
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
def validate_pending_reposts(self):
if self.acc_frozen_upto:
check_pending_reposting(self.acc_frozen_upto)
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)

View File

@@ -5,4 +5,4 @@ frappe.ui.form.on('Accounts Settings', {
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
frm.set_df_property("credit_controller", "label", "Credit Manager");
}
});
});

View File

@@ -1,4 +1,4 @@
from __future__ import unicode_literals
import unittest
import frappe

View File

@@ -1,56 +0,0 @@
{
"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"
}

View File

@@ -25,7 +25,8 @@
"allocated_amount",
"column_break_13",
"base_tax_amount",
"base_total"
"base_total",
"base_allocated_amount"
],
"fields": [
{
@@ -167,6 +168,12 @@
"label": "Allocated Amount",
"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",
@@ -179,7 +186,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-11-25 11:10:10.945027",
"modified": "2021-06-09 11:46:58.373170",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Advance Taxes and Charges",

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -120,4 +120,4 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
plaid_success(token, response) {
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
}
};
};

View File

@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.contacts.address_and_contact import (
delete_contact_and_address,
load_address_and_contact,
)
from __future__ import unicode_literals
import frappe
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):
@@ -15,4 +13,4 @@ class Bank(Document):
load_address_and_contact(self)
def on_trash(self):
delete_contact_and_address('Bank', self.name)
delete_contact_and_address('Bank', self.name)

View File

@@ -1,3 +1,4 @@
from __future__ import unicode_literals
from frappe import _

View File

@@ -0,0 +1,23 @@
/* 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()
]);
});

View File

@@ -1,8 +1,10 @@
# -*- 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

View File

@@ -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 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):

View File

@@ -1,3 +1,4 @@
from __future__ import unicode_literals
from frappe import _
@@ -25,4 +26,4 @@ def get_data():
'items': ['Journal Entry']
}
]
}
}

View File

@@ -0,0 +1,23 @@
/* 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()
]);
});

View File

@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
from __future__ import unicode_literals
import frappe
from frappe import ValidationError
from frappe import _
from frappe import ValidationError
import unittest
# test_records = frappe.get_test_records('Bank Account')

View File

@@ -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

View File

@@ -0,0 +1,23 @@
/* 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()
]);
});

View File

@@ -1,8 +1,9 @@
# -*- 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

View File

@@ -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

View File

@@ -1,9 +1,10 @@
# -*- 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

View File

@@ -8,7 +8,7 @@ frappe.ui.form.on("Bank Clearance", {
onload: function(frm) {
let default_bank_account = frappe.defaults.get_user_default("Company")?
let default_bank_account = frappe.defaults.get_user_default("Company")?
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
frm.set_value("account", default_bank_account);

View File

@@ -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 _, msgprint
from frappe.utils import flt, getdate, nowdate, fmt_money
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"

View File

@@ -1,9 +1,10 @@
# -*- 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

View File

@@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@@ -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
pass

View File

@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import frappe
from __future__ import unicode_literals
import frappe, json
from frappe.model.document import Document
from frappe import _
from frappe.desk.search import sanitize_searchfield
from frappe.model.document import Document
class BankGuarantee(Document):
def validate(self):
@@ -27,6 +25,6 @@ class BankGuarantee(Document):
def get_vouchar_detials(column_list, doctype, docname):
column_list = json.loads(column_list)
for col in column_list:
sanitize_searchfield(col)
sanitize_searchfield(col)
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]

View File

@@ -0,0 +1,23 @@
/* 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()
]);
});

View File

@@ -1,8 +1,10 @@
# -*- 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

View File

@@ -7,17 +7,13 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
frm.set_query("bank_account", function () {
return {
filters: {
company: frm.doc.company,
company: ["in", frm.doc.company],
'is_company_account': 1
},
};
});
},
onload: function (frm) {
frm.trigger('bank_account');
},
refresh: function (frm) {
frappe.require("assets/js/bank-reconciliation-tool.min.js", () =>
frm.trigger("make_reconciliation_tool")
@@ -55,7 +51,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
bank_account: function (frm) {
frappe.db.get_value(
"Bank Account",
frm.doc.bank_account,
frm.bank_account,
"account",
(r) => {
frappe.db.get_value(
@@ -64,7 +60,6 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
"account_currency",
(r) => {
frm.currency = r.account_currency;
frm.trigger("render_chart");
}
);
}
@@ -129,7 +124,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
}
},
render_chart: frappe.utils.debounce((frm) => {
render_chart(frm) {
frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager(
{
$reconciliation_tool_cards: frm.get_field(
@@ -141,7 +136,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
currency: frm.currency,
}
);
}, 500),
},
render(frm) {
if (frm.doc.bank_account) {

View File

@@ -1,21 +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 json
import frappe
from frappe import _
from frappe.model.document import Document
from frappe import _
from frappe.utils import flt
from erpnext import get_company_currency
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
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
class BankReconciliationTool(Document):
@@ -218,8 +216,6 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
company_account = frappe.db.get_value('Bank Account', transaction.bank_account, 'account')
if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled"))
total_amount = 0
@@ -228,7 +224,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
total_amount += get_paid_amount(frappe._dict({
'payment_document': voucher['payment_doctype'],
'payment_entry': voucher['payment_name'],
}), transaction.currency, company_account)
}), transaction.currency)
if total_amount > transaction.unallocated_amount:
frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
@@ -263,7 +259,7 @@ def get_linked_payments(bank_transaction_name, document_types = None):
return matching
def check_matching(bank_account, company, transaction, document_types):
# combine all types of vouchers
# combine all types of vocuhers
subquery = get_queries(bank_account, company, transaction, document_types)
filters = {
"amount": transaction.unallocated_amount,
@@ -344,13 +340,7 @@ def get_pe_matching_query(amount_condition, account_from_to, transaction):
def get_je_matching_query(amount_condition, transaction):
# get matching journal entry query
# We have mapping at the bank level
# So one bank could have both types of bank accounts like asset and liability
# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
company_account = frappe.get_value("Bank Account", transaction.bank_account, "account")
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
return f"""
SELECT
@@ -434,7 +424,7 @@ def get_pi_matching_query(amount_condition):
def get_ec_matching_query(bank_account, company, amount_condition):
# get matching Expense Claim query
mode_of_payments = [x["parent"] for x in frappe.db.get_all("Mode of Payment Account",
mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account",
filters={"default_account": bank_account}, fields=["parent"])]
mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
company_currency = get_company_currency(company)

View File

@@ -1,9 +1,10 @@
# -*- 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

View File

@@ -239,8 +239,7 @@ frappe.ui.form.on("Bank Statement Import", {
"withdrawal",
"description",
"reference_number",
"bank_account",
"currency"
"bank_account"
],
},
});

View File

@@ -1,23 +1,24 @@
# -*- 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 frappe
import openpyxl
from frappe import _
from frappe.core.doctype.data_import.data_import import DataImport
from frappe.core.doctype.data_import.importer import Importer, ImportFile
from frappe.utils.background_jobs import enqueue
from frappe.utils.xlsxutils import ILLEGAL_CHARACTERS_RE, handle_html
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter
from six import string_types
INVALID_VALUES = ("", None)
import frappe
from frappe.core.doctype.data_import.importer import Importer, ImportFile
from frappe.utils.background_jobs import enqueue
from frappe.utils.xlsxutils import handle_html, ILLEGAL_CHARACTERS_RE
from frappe import _
from frappe.core.doctype.data_import.data_import import DataImport
class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs):
@@ -97,18 +98,6 @@ def download_errored_template(data_import_name):
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows()
def parse_data_from_template(raw_data):
data = []
for i, row in enumerate(raw_data):
if all(v in INVALID_VALUES for v in row):
# empty row
continue
data.append(row)
return data
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
"""This method runs in background job"""
@@ -118,8 +107,7 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
file = import_file_path if import_file_path else google_sheets_url
import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
data = parse_data_from_template(import_file.raw_data)
data = import_file.raw_data
if import_file_path:
add_bank_account(data, bank_account)

View File

@@ -1,9 +1,10 @@
# -*- 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

View File

@@ -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 erpnext.controllers.status_updater import StatusUpdater
from frappe import _
class BankTransaction(StatusUpdater):
def after_insert(self):
@@ -22,10 +22,6 @@ class BankTransaction(StatusUpdater):
self.clear_linked_payment_entries()
self.set_status(update=True)
def on_cancel(self):
self.clear_linked_payment_entries(for_cancel=True)
self.set_status(update=True)
def update_allocations(self):
if self.payment_entries:
allocated_amount = reduce(lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries])
@@ -46,45 +42,20 @@ class BankTransaction(StatusUpdater):
self.reload()
def clear_linked_payment_entries(self, for_cancel=False):
def clear_linked_payment_entries(self):
for payment_entry in self.payment_entries:
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
self.clear_simple_entry(payment_entry)
elif payment_entry.payment_document == "Sales Invoice":
self.clear_sales_invoice(payment_entry, for_cancel=for_cancel)
self.clear_sales_invoice(payment_entry)
def clear_simple_entry(self, payment_entry, for_cancel=False):
if payment_entry.payment_document == "Payment Entry":
if frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type") == "Internal Transfer":
if len(get_reconciled_bank_transactions(payment_entry)) < 2:
return
def clear_simple_entry(self, payment_entry):
frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
payment_entry.payment_document, payment_entry.payment_entry,
"clearance_date", clearance_date)
def clear_sales_invoice(self, payment_entry, for_cancel=False):
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
"Sales Invoice Payment",
dict(
parenttype=payment_entry.payment_document,
parent=payment_entry.payment_entry
),
"clearance_date", clearance_date)
def get_reconciled_bank_transactions(payment_entry):
reconciled_bank_transactions = frappe.get_all(
'Bank Transaction Payments',
filters = {
'payment_entry': payment_entry.payment_entry
},
fields = ['parent']
)
return reconciled_bank_transactions
def clear_sales_invoice(self, payment_entry):
frappe.db.set_value("Sales Invoice Payment", dict(parenttype=payment_entry.payment_document,
parent=payment_entry.payment_entry), "clearance_date", self.date)
def get_total_allocated_amount(payment_entry):
return frappe.db.sql("""
@@ -102,7 +73,7 @@ def get_total_allocated_amount(payment_entry):
AND
bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
def get_paid_amount(payment_entry, currency, bank_account):
def get_paid_amount(payment_entry, currency):
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
paid_amount_field = "paid_amount"
@@ -115,7 +86,7 @@ def get_paid_amount(payment_entry, currency, bank_account):
payment_entry.payment_entry, paid_amount_field)
elif payment_entry.payment_document == "Journal Entry":
return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account}, "sum(credit_in_account_currency)")
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_credit")
elif payment_entry.payment_document == "Expense Claim":
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")
@@ -134,3 +105,4 @@ def unclear_reference_payment(doctype, docname):
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
return doc.payment_entry

View File

@@ -4,12 +4,10 @@
frappe.listview_settings['Bank Transaction'] = {
add_fields: ["unallocated_amount"],
get_indicator: function(doc) {
if(doc.docstatus == 2) {
return [__("Cancelled"), "red", "docstatus,=,2"];
if(flt(doc.unallocated_amount)>0) {
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
} else if(flt(doc.unallocated_amount)<=0) {
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
} else if(flt(doc.unallocated_amount)>0) {
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
}
}
};
};

View File

@@ -1,15 +1,14 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
from __future__ import unicode_literals
import frappe
import json
from frappe.utils import getdate
from frappe.utils.dateutils import parse_date
from six import iteritems
@frappe.whitelist()
def upload_bank_statement():
if getattr(frappe, "uploaded_file", None):
@@ -78,4 +77,4 @@ def get_bank_mapping(bank_account):
mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
return mapping
return mapping

View File

@@ -0,0 +1,23 @@
/* 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()
]);
});

View File

@@ -1,19 +1,16 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import json
import unittest
from __future__ import unicode_literals
import frappe
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
get_linked_payments,
reconcile_vouchers,
)
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
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.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
test_dependencies = ["Item", "Cost Center"]
@@ -28,8 +25,7 @@ class TestBankTransaction(unittest.TestCase):
def tearDownClass(cls):
for bt in frappe.get_all("Bank Transaction"):
doc = frappe.get_doc("Bank Transaction", bt.name)
if doc.docstatus == 1:
doc.cancel()
doc.cancel()
doc.delete()
# Delete directly in DB to avoid validation errors for countries not allowing deletion
@@ -61,12 +57,6 @@ class TestBankTransaction(unittest.TestCase):
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
self.assertTrue(clearance_date is not None)
bank_transaction.reload()
bank_transaction.cancel()
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
self.assertFalse(clearance_date)
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
def test_debit_credit_output(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))

Some files were not shown because too many files have changed in this diff Show More