Compare commits
274 Commits
fix-error-
...
enconnex_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65771b431b | ||
|
|
25e4af5b03 | ||
|
|
ae04395caf | ||
|
|
11e4760530 | ||
|
|
171519a4b8 | ||
|
|
984d6b2d39 | ||
|
|
4f6b760cfd | ||
|
|
ca8fe28138 | ||
|
|
5b9e0b5b57 | ||
|
|
c7349145c5 | ||
|
|
3875f23d9a | ||
|
|
b5ea536530 | ||
|
|
ac61a86021 | ||
|
|
a5b7d5ecf0 | ||
|
|
fda58abfaf | ||
|
|
86e700b24f | ||
|
|
80b57e7c41 | ||
|
|
900adeefda | ||
|
|
36c9a8ab08 | ||
|
|
8288b5a302 | ||
|
|
d27e663b2a | ||
|
|
a82ecf8c07 | ||
|
|
427f9b6367 | ||
|
|
e42f423ed8 | ||
|
|
e4274c1cc5 | ||
|
|
1e7133baf1 | ||
|
|
2c56ae78a7 | ||
|
|
666af17cfa | ||
|
|
88da5f9073 | ||
|
|
1dd3396b60 | ||
|
|
153323ee41 | ||
|
|
09ef7b62a1 | ||
|
|
e2e2a305da | ||
|
|
c648027473 | ||
|
|
14096fdfc8 | ||
|
|
4bc4e96c2a | ||
|
|
b8d5db6689 | ||
|
|
4b9aed2c0e | ||
|
|
7a3dbe1b72 | ||
|
|
cf23f83d6c | ||
|
|
f0816acfc2 | ||
|
|
d73f1a8170 | ||
|
|
9faaa8ffd8 | ||
|
|
afb60c3678 | ||
|
|
542c959257 | ||
|
|
70d5d2cb2b | ||
|
|
c55bbc2db9 | ||
|
|
f9c13bf32b | ||
|
|
03107ffedd | ||
|
|
68c0427aef | ||
|
|
e6d9d47cb4 | ||
|
|
afbe93de3c | ||
|
|
62895a4ca5 | ||
|
|
868b816c5a | ||
|
|
66773cabef | ||
|
|
a44db68b8e | ||
|
|
6b198241e1 | ||
|
|
a9f3c3e50a | ||
|
|
ba563ca232 | ||
|
|
103ce8bbb7 | ||
|
|
93c67ffd92 | ||
|
|
491987b8d3 | ||
|
|
552330f744 | ||
|
|
8e01c8aefb | ||
|
|
ff57fbe2c1 | ||
|
|
fb0936be50 | ||
|
|
ed369c105f | ||
|
|
192002e156 | ||
|
|
52ac8484ff | ||
|
|
bcb7848c84 | ||
|
|
19ca4d000b | ||
|
|
288082abfd | ||
|
|
2b1809bd0e | ||
|
|
5fab55a564 | ||
|
|
799f0daa1e | ||
|
|
2f4b91172d | ||
|
|
adb4225fa7 | ||
|
|
fdcc926597 | ||
|
|
04fa2e8d29 | ||
|
|
0c6d69e27c | ||
|
|
12c957b2b2 | ||
|
|
11cef55649 | ||
|
|
1585986fa4 | ||
|
|
10c069588f | ||
|
|
ca966601a2 | ||
|
|
655a57513f | ||
|
|
a4fabefee7 | ||
|
|
5bb15b08d9 | ||
|
|
f045eb3135 | ||
|
|
62890adef4 | ||
|
|
f000b3b362 | ||
|
|
1a8df8f112 | ||
|
|
760e6cebd4 | ||
|
|
7835ec6a6c | ||
|
|
5c7333bd3a | ||
|
|
a821647283 | ||
|
|
f6ffdcef98 | ||
|
|
71976de79c | ||
|
|
3f6f20b0d2 | ||
|
|
d9a6a6fe6a | ||
|
|
1cfb69aaae | ||
|
|
50cd7a1650 | ||
|
|
f4a4efb510 | ||
|
|
7337669e49 | ||
|
|
fc34ded7a8 | ||
|
|
342299fe3b | ||
|
|
5307f8e794 | ||
|
|
540c119a27 | ||
|
|
21479fc5ea | ||
|
|
2c12124550 | ||
|
|
f68e85519c | ||
|
|
0868bf5d45 | ||
|
|
8bc69d3fe0 | ||
|
|
cd3d6ade04 | ||
|
|
9070ec3237 | ||
|
|
cfcb1a15f8 | ||
|
|
d0ee3fc0cd | ||
|
|
776d89c4de | ||
|
|
a02cad5ec4 | ||
|
|
837aae7bcb | ||
|
|
cfcc9eac39 | ||
|
|
4b86f0440e | ||
|
|
f709d57475 | ||
|
|
377fe373e2 | ||
|
|
39ba31e68f | ||
|
|
6d3e843f07 | ||
|
|
a679d43722 | ||
|
|
2466c3214a | ||
|
|
646ead65d8 | ||
|
|
7b282d253f | ||
|
|
37320074b2 | ||
|
|
a5353c7d2d | ||
|
|
e85050c190 | ||
|
|
c3b22c6e5c | ||
|
|
73cef56df7 | ||
|
|
a0f6381d6e | ||
|
|
b7b3944cef | ||
|
|
9d4bf040e9 | ||
|
|
afef92a88b | ||
|
|
a9f4508cd1 | ||
|
|
dac19cbca5 | ||
|
|
573d9094bb | ||
|
|
0413e34f0d | ||
|
|
7d2c5c24c6 | ||
|
|
107e2cdaba | ||
|
|
a2bf727794 | ||
|
|
ed93d41727 | ||
|
|
7b458be170 | ||
|
|
9d806df589 | ||
|
|
dd2f6aade1 | ||
|
|
0f30c52759 | ||
|
|
610eb508bf | ||
|
|
047bf3833e | ||
|
|
e4554f690c | ||
|
|
57d79a8149 | ||
|
|
65fb0729d2 | ||
|
|
692c617cb2 | ||
|
|
614c04e357 | ||
|
|
07e86e0c3d | ||
|
|
da9418b378 | ||
|
|
8e641d353c | ||
|
|
e00f52c455 | ||
|
|
c4bdbe1046 | ||
|
|
4d6c53f7b2 | ||
|
|
94aad17fd5 | ||
|
|
08d562a5bb | ||
|
|
509e40a584 | ||
|
|
cb80aaf707 | ||
|
|
0ce888ec08 | ||
|
|
4569c9b5e3 | ||
|
|
e895ddc952 | ||
|
|
3a02fd9bda | ||
|
|
fb4ad9042c | ||
|
|
d2b44e90d6 | ||
|
|
094328b8c9 | ||
|
|
317a0495e2 | ||
|
|
e3f9c5246e | ||
|
|
c56d775541 | ||
|
|
75037e4493 | ||
|
|
465fc5163f | ||
|
|
9c88c37102 | ||
|
|
f3bf9aa70f | ||
|
|
9765809264 | ||
|
|
0803102a92 | ||
|
|
34650b7122 | ||
|
|
28ec74d35e | ||
|
|
fc45f9dc17 | ||
|
|
8ade3b25ab | ||
|
|
dbb378f759 | ||
|
|
62cd0c6592 | ||
|
|
97dafc06f9 | ||
|
|
dc98db611b | ||
|
|
bf5a791d0e | ||
|
|
b8b66a1450 | ||
|
|
e1b7d465c4 | ||
|
|
db2d221976 | ||
|
|
383a3df807 | ||
|
|
bc3b5e334b | ||
|
|
b855a32d84 | ||
|
|
8fe07e4567 | ||
|
|
da0c064c70 | ||
|
|
f3d74b7638 | ||
|
|
529727224c | ||
|
|
9546e3ea32 | ||
|
|
6f035d2169 | ||
|
|
8d612d3f78 | ||
|
|
c1acb7b77a | ||
|
|
e0ef4abbcf | ||
|
|
bd16e0885b | ||
|
|
35f985e514 | ||
|
|
b32de3ab13 | ||
|
|
4c420ca490 | ||
|
|
469c23b562 | ||
|
|
3fe127d483 | ||
|
|
78b30ccc4f | ||
|
|
10fdd38e32 | ||
|
|
dc9438c3a4 | ||
|
|
6771d97c42 | ||
|
|
ec17111e27 | ||
|
|
8679ce475a | ||
|
|
fd3952ba9a | ||
|
|
5138f00176 | ||
|
|
6a24d2df8c | ||
|
|
96bd2f72d0 | ||
|
|
fb33f7d3c3 | ||
|
|
5e0817f57a | ||
|
|
f6d23df826 | ||
|
|
499273e64a | ||
|
|
14c3715fef | ||
|
|
d43d7a0a58 | ||
|
|
67e68670bb | ||
|
|
9dcab60d61 | ||
|
|
30a7da51d3 | ||
|
|
df859fdc23 | ||
|
|
48527c48d2 | ||
|
|
df09c76148 | ||
|
|
0ce26ce90d | ||
|
|
a9f65654a1 | ||
|
|
57caf99a7e | ||
|
|
d729f7f50e | ||
|
|
6fce734476 | ||
|
|
354da7ad4c | ||
|
|
aaf445af98 | ||
|
|
6eb4fe6659 | ||
|
|
3739dc3f78 | ||
|
|
dc3c03e47e | ||
|
|
2fbc8c8f30 | ||
|
|
06a21da29e | ||
|
|
b488872cbd | ||
|
|
88fa9866b1 | ||
|
|
62109c3c1a | ||
|
|
1bbdc504bf | ||
|
|
e43a50357b | ||
|
|
17cd44ecd7 | ||
|
|
54ebb06d8a | ||
|
|
ddf9d9ae0e | ||
|
|
5488ccfa8c | ||
|
|
0b304b3561 | ||
|
|
4971061054 | ||
|
|
a7167f775d | ||
|
|
1ccba26173 | ||
|
|
ffbf30809d | ||
|
|
ad02cfe98e | ||
|
|
392a2856d3 | ||
|
|
56b0136696 | ||
|
|
da82a380e1 | ||
|
|
c0e3bcf865 | ||
|
|
398c0f0b7f | ||
|
|
6b9f7e35f4 | ||
|
|
f9a6a90900 | ||
|
|
9ebe8d5895 | ||
|
|
24c5fc862d | ||
|
|
0c92e7f9a1 | ||
|
|
c788ee28c2 |
@@ -92,7 +92,6 @@
|
||||
"cur_page": true,
|
||||
"cur_list": true,
|
||||
"cur_tree": true,
|
||||
"cur_pos": true,
|
||||
"msg_dialog": true,
|
||||
"is_null": true,
|
||||
"in_list": true,
|
||||
@@ -147,15 +146,9 @@
|
||||
"Chart": true,
|
||||
"Cypress": true,
|
||||
"cy": true,
|
||||
"describe": true,
|
||||
"expect": true,
|
||||
"it": true,
|
||||
"context": true,
|
||||
"before": true,
|
||||
"beforeEach": true,
|
||||
"onScan": true,
|
||||
"html2canvas": true,
|
||||
"extend_cscript": true,
|
||||
"localforage": true
|
||||
"beforeEach": true
|
||||
}
|
||||
}
|
||||
|
||||
33
.flake8
33
.flake8
@@ -1,33 +0,0 @@
|
||||
[flake8]
|
||||
ignore =
|
||||
E121,
|
||||
E126,
|
||||
E127,
|
||||
E128,
|
||||
E203,
|
||||
E225,
|
||||
E226,
|
||||
E231,
|
||||
E241,
|
||||
E251,
|
||||
E261,
|
||||
E265,
|
||||
E302,
|
||||
E303,
|
||||
E305,
|
||||
E402,
|
||||
E501,
|
||||
E741,
|
||||
W291,
|
||||
W292,
|
||||
W293,
|
||||
W391,
|
||||
W503,
|
||||
W504,
|
||||
F403,
|
||||
B007,
|
||||
B950,
|
||||
W191,
|
||||
|
||||
max-line-length = 200
|
||||
exclude=.github/helper/semgrep_rules
|
||||
@@ -1,19 +0,0 @@
|
||||
# Since version 2.23 (released in August 2019), git-blame has a feature
|
||||
# to ignore or bypass certain commits.
|
||||
#
|
||||
# This file contains a list of commits that are not likely what you
|
||||
# are looking for in a blame, such as mass reformatting or renaming.
|
||||
# You can set this file as a default ignore file for blame by running
|
||||
# the following command.
|
||||
#
|
||||
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
|
||||
# This commit just changes spaces to tabs for indentation in some files
|
||||
5f473611bd6ed57703716244a054d3fb5ba9cd23
|
||||
|
||||
# Whitespace trimming throughout codebase
|
||||
9bb69e711a5da43aaf8c8ecb5601aeffd89dbe5a
|
||||
f0bcb753fb7ebbb64bb0d6906d431d002f0f7d8f
|
||||
|
||||
# imports cleanup
|
||||
4b2be2999f2203493b49bf74c5b440d49e38b5e3
|
||||
72
.github/helper/.flake8_strict
vendored
72
.github/helper/.flake8_strict
vendored
@@ -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
|
||||
16
.github/helper/documentation.py
vendored
16
.github/helper/documentation.py
vendored
@@ -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! 🎉")
|
||||
|
||||
|
||||
41
.github/helper/install.sh
vendored
41
.github/helper/install.sh
vendored
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd ~ || exit
|
||||
|
||||
sudo apt-get install redis-server libcups2-dev
|
||||
|
||||
pip install frappe-bench
|
||||
|
||||
git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" --depth 1
|
||||
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
|
||||
|
||||
mkdir ~/frappe-bench/sites/test_site
|
||||
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/
|
||||
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
|
||||
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe"
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
|
||||
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES"
|
||||
|
||||
wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
|
||||
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
|
||||
|
||||
cd ~/frappe-bench || exit
|
||||
|
||||
sed -i 's/watch:/# watch:/g' Procfile
|
||||
sed -i 's/schedule:/# schedule:/g' Procfile
|
||||
sed -i 's/socketio:/# socketio:/g' Procfile
|
||||
sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
|
||||
|
||||
bench get-app erpnext "${GITHUB_WORKSPACE}"
|
||||
bench start &> bench_run_logs.txt &
|
||||
bench --site test_site reinstall --yes
|
||||
38
.github/helper/semgrep_rules/README.md
vendored
38
.github/helper/semgrep_rules/README.md
vendored
@@ -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
|
||||
@@ -1,63 +0,0 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
# ruleid: frappe-modifying-but-not-comitting
|
||||
def on_submit(self):
|
||||
if self.value_of_goods == 0:
|
||||
frappe.throw(_('Value of goods cannot be 0'))
|
||||
self.status = 'Submitted'
|
||||
|
||||
|
||||
# ok: frappe-modifying-but-not-comitting
|
||||
def on_submit(self):
|
||||
if self.value_of_goods == 0:
|
||||
frappe.throw(_('Value of goods cannot be 0'))
|
||||
self.status = 'Submitted'
|
||||
self.db_set('status', 'Submitted')
|
||||
|
||||
# ok: frappe-modifying-but-not-comitting
|
||||
def on_submit(self):
|
||||
if self.value_of_goods == 0:
|
||||
frappe.throw(_('Value of goods cannot be 0'))
|
||||
x = "y"
|
||||
self.status = x
|
||||
self.db_set('status', x)
|
||||
|
||||
|
||||
# ok: frappe-modifying-but-not-comitting
|
||||
def on_submit(self):
|
||||
x = "y"
|
||||
self.status = x
|
||||
self.save()
|
||||
|
||||
# ruleid: frappe-modifying-but-not-comitting-other-method
|
||||
class DoctypeClass(Document):
|
||||
def on_submit(self):
|
||||
self.good_method()
|
||||
self.tainted_method()
|
||||
|
||||
def tainted_method(self):
|
||||
self.status = "uptate"
|
||||
|
||||
|
||||
# ok: frappe-modifying-but-not-comitting-other-method
|
||||
class DoctypeClass(Document):
|
||||
def on_submit(self):
|
||||
self.good_method()
|
||||
self.tainted_method()
|
||||
|
||||
def tainted_method(self):
|
||||
self.status = "update"
|
||||
self.db_set("status", "update")
|
||||
|
||||
# ok: frappe-modifying-but-not-comitting-other-method
|
||||
class DoctypeClass(Document):
|
||||
def on_submit(self):
|
||||
self.good_method()
|
||||
self.tainted_method()
|
||||
self.save()
|
||||
|
||||
def tainted_method(self):
|
||||
self.status = "uptate"
|
||||
151
.github/helper/semgrep_rules/frappe_correctness.yml
vendored
151
.github/helper/semgrep_rules/frappe_correctness.yml
vendored
@@ -1,151 +0,0 @@
|
||||
# This file specifies rules for correctness according to how frappe doctype data model works.
|
||||
|
||||
rules:
|
||||
- id: frappe-modifying-but-not-comitting
|
||||
patterns:
|
||||
- pattern: |
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = ...
|
||||
- pattern-not: |
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = ...
|
||||
...
|
||||
self.db_set(..., self.$ATTR, ...)
|
||||
- pattern-not: |
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = $SOME_VAR
|
||||
...
|
||||
self.db_set(..., $SOME_VAR, ...)
|
||||
- pattern-not: |
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = $SOME_VAR
|
||||
...
|
||||
self.save()
|
||||
- metavariable-regex:
|
||||
metavariable: '$ATTR'
|
||||
# this is negative look-ahead, add more attrs to ignore like (ignore|ignore_this_too|ignore_me)
|
||||
regex: '^(?!ignore_linked_doctypes|status_updater)(.*)$'
|
||||
- metavariable-regex:
|
||||
metavariable: "$METHOD"
|
||||
regex: "(on_submit|on_cancel)"
|
||||
message: |
|
||||
DocType modified in self.$METHOD. Please check if modification of self.$ATTR is commited to database.
|
||||
languages: [python]
|
||||
severity: ERROR
|
||||
|
||||
- id: frappe-modifying-but-not-comitting-other-method
|
||||
patterns:
|
||||
- pattern: |
|
||||
class $DOCTYPE(...):
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ANOTHER_METHOD()
|
||||
...
|
||||
|
||||
def $ANOTHER_METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = ...
|
||||
- pattern-not: |
|
||||
class $DOCTYPE(...):
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ANOTHER_METHOD()
|
||||
...
|
||||
|
||||
def $ANOTHER_METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = ...
|
||||
...
|
||||
self.db_set(..., self.$ATTR, ...)
|
||||
- pattern-not: |
|
||||
class $DOCTYPE(...):
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ANOTHER_METHOD()
|
||||
...
|
||||
|
||||
def $ANOTHER_METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = $SOME_VAR
|
||||
...
|
||||
self.db_set(..., $SOME_VAR, ...)
|
||||
- pattern-not: |
|
||||
class $DOCTYPE(...):
|
||||
def $METHOD(self, ...):
|
||||
...
|
||||
self.$ANOTHER_METHOD()
|
||||
...
|
||||
self.save()
|
||||
def $ANOTHER_METHOD(self, ...):
|
||||
...
|
||||
self.$ATTR = ...
|
||||
- metavariable-regex:
|
||||
metavariable: "$METHOD"
|
||||
regex: "(on_submit|on_cancel)"
|
||||
message: |
|
||||
self.$ANOTHER_METHOD is called from self.$METHOD, check if changes to self.$ATTR are commited to database.
|
||||
languages: [python]
|
||||
severity: ERROR
|
||||
|
||||
- id: frappe-print-function-in-doctypes
|
||||
pattern: print(...)
|
||||
message: |
|
||||
Did you mean to leave this print statement in? Consider using msgprint or logger instead of print statement.
|
||||
languages: [python]
|
||||
severity: WARNING
|
||||
paths:
|
||||
include:
|
||||
- "*/**/doctype/*"
|
||||
|
||||
- id: frappe-modifying-child-tables-while-iterating
|
||||
pattern-either:
|
||||
- pattern: |
|
||||
for $ROW in self.$TABLE:
|
||||
...
|
||||
self.remove(...)
|
||||
- pattern: |
|
||||
for $ROW in self.$TABLE:
|
||||
...
|
||||
self.append(...)
|
||||
message: |
|
||||
Child table being modified while iterating on it.
|
||||
languages: [python]
|
||||
severity: ERROR
|
||||
paths:
|
||||
include:
|
||||
- "*/**/doctype/*"
|
||||
|
||||
- id: frappe-same-key-assigned-twice
|
||||
pattern-either:
|
||||
- pattern: |
|
||||
{..., $X: $A, ..., $X: $B, ...}
|
||||
- pattern: |
|
||||
dict(..., ($X, $A), ..., ($X, $B), ...)
|
||||
- pattern: |
|
||||
_dict(..., ($X, $A), ..., ($X, $B), ...)
|
||||
message: |
|
||||
key `$X` is uselessly assigned twice. This could be a potential bug.
|
||||
languages: [python]
|
||||
severity: ERROR
|
||||
|
||||
|
||||
- id: frappe-manual-commit
|
||||
patterns:
|
||||
- pattern: frappe.db.commit()
|
||||
- pattern-not-inside: |
|
||||
try:
|
||||
...
|
||||
except ...:
|
||||
...
|
||||
message: |
|
||||
Manually commiting a transaction is highly discouraged. Read about the transaction model implemented by Frappe Framework before adding manual commits: https://frappeframework.com/docs/user/en/api/database#database-transaction-model If you think manual commit is required then add a comment explaining why and `// nosemgrep` on the same line.
|
||||
paths:
|
||||
exclude:
|
||||
- "**/patches/**"
|
||||
- "**/demo/**"
|
||||
languages: [python]
|
||||
severity: ERROR
|
||||
14
.github/helper/semgrep_rules/report.py
vendored
14
.github/helper/semgrep_rules/report.py
vendored
@@ -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"))
|
||||
34
.github/helper/semgrep_rules/report.yml
vendored
34
.github/helper/semgrep_rules/report.yml
vendored
@@ -1,34 +0,0 @@
|
||||
rules:
|
||||
- id: frappe-missing-translate-function-in-report-python
|
||||
paths:
|
||||
include:
|
||||
- "**/report"
|
||||
exclude:
|
||||
- "**/regional"
|
||||
pattern-either:
|
||||
- patterns:
|
||||
- pattern: |
|
||||
{..., "label": "...", ...}
|
||||
- pattern-not: |
|
||||
{..., "label": _("..."), ...}
|
||||
- patterns:
|
||||
- pattern: dict(..., label="...", ...)
|
||||
- pattern-not: dict(..., label=_("..."), ...)
|
||||
message: |
|
||||
All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
|
||||
languages: [python]
|
||||
severity: ERROR
|
||||
|
||||
- id: frappe-translated-values-in-business-logic
|
||||
paths:
|
||||
include:
|
||||
- "**/report"
|
||||
patterns:
|
||||
- pattern-inside: |
|
||||
{..., filters: [...], ...}
|
||||
- pattern: |
|
||||
{..., options: [..., __("..."), ...], ...}
|
||||
message: |
|
||||
Using translated values in options field will require you to translate the values while comparing in business logic. Instead of passing translated labels provide objects that contain both label and value. e.g. { label: __("Option value"), value: "Option value"}
|
||||
languages: [javascript]
|
||||
severity: ERROR
|
||||
6
.github/helper/semgrep_rules/security.py
vendored
6
.github/helper/semgrep_rules/security.py
vendored
@@ -1,6 +0,0 @@
|
||||
def function_name(input):
|
||||
# ruleid: frappe-codeinjection-eval
|
||||
eval(input)
|
||||
|
||||
# ok: frappe-codeinjection-eval
|
||||
eval("1 + 1")
|
||||
10
.github/helper/semgrep_rules/security.yml
vendored
10
.github/helper/semgrep_rules/security.yml
vendored
@@ -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
|
||||
44
.github/helper/semgrep_rules/translate.js
vendored
44
.github/helper/semgrep_rules/translate.js
vendored
@@ -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])
|
||||
61
.github/helper/semgrep_rules/translate.py
vendored
61
.github/helper/semgrep_rules/translate.py
vendored
@@ -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
|
||||
64
.github/helper/semgrep_rules/translate.yml
vendored
64
.github/helper/semgrep_rules/translate.yml
vendored
@@ -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
|
||||
9
.github/helper/semgrep_rules/ux.js
vendored
9
.github/helper/semgrep_rules/ux.js
vendored
@@ -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") }}. ');
|
||||
30
.github/helper/semgrep_rules/ux.py
vendored
30
.github/helper/semgrep_rules/ux.py
vendored
@@ -1,30 +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"))
|
||||
30
.github/helper/semgrep_rules/ux.yml
vendored
30
.github/helper/semgrep_rules/ux.yml
vendored
@@ -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
|
||||
10
.github/helper/translation.py
vendored
10
.github/helper/translation.py
vendored
@@ -2,7 +2,7 @@ import re
|
||||
import sys
|
||||
|
||||
errors_encounter = 0
|
||||
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
|
||||
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
|
||||
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]")
|
||||
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}")
|
||||
f_string_pattern = re.compile(r"_\(f[\"']")
|
||||
@@ -28,7 +28,7 @@ for _file in files_to_scan:
|
||||
has_f_string = f_string_pattern.search(line)
|
||||
if has_f_string:
|
||||
errors_encounter += 1
|
||||
print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}')
|
||||
print(f'\nF-strings are not supported for translations at line number {line_number + 1}\n{line.strip()[:100]}')
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
@@ -36,7 +36,7 @@ for _file in files_to_scan:
|
||||
match = pattern.search(line)
|
||||
error_found = False
|
||||
|
||||
if not match and line.endswith((',\n', '[\n')):
|
||||
if not match and line.endswith(',\n'):
|
||||
# concat remaining text to validate multiline pattern
|
||||
line = "".join(file_lines[line_number - 1:])
|
||||
line = line[start_matches.start() + 1:]
|
||||
@@ -44,11 +44,11 @@ for _file in files_to_scan:
|
||||
|
||||
if not match:
|
||||
error_found = True
|
||||
print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}')
|
||||
print(f'\nTranslation syntax error at line number {line_number + 1}\n{line.strip()[:100]}')
|
||||
|
||||
if not error_found and not words_pattern.search(line):
|
||||
error_found = True
|
||||
print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}')
|
||||
print(f'\nTranslation is useless because it has no words at line number {line_number + 1}\n{line.strip()[:100]}')
|
||||
|
||||
if error_found:
|
||||
errors_encounter += 1
|
||||
|
||||
23
.github/workflows/backport.yml
vendored
23
.github/workflows/backport.yml
vendored
@@ -1,25 +1,16 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
backport:
|
||||
runs-on: ubuntu-18.04
|
||||
name: Backport
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
- name: Backport
|
||||
uses: tibdex/backport@v1
|
||||
with:
|
||||
repository: "frappe/backport"
|
||||
path: ./actions
|
||||
ref: develop
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Run backport
|
||||
uses: ./actions/backport
|
||||
with:
|
||||
token: ${{secrets.BACKPORT_BOT_TOKEN}}
|
||||
labelsToAdd: "backport"
|
||||
title: "{{originalTitle}}"
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
27
.github/workflows/linters.yml
vendored
27
.github/workflows/linters.yml
vendored
@@ -1,27 +0,0 @@
|
||||
name: Linters
|
||||
|
||||
on:
|
||||
pull_request: { }
|
||||
|
||||
jobs:
|
||||
|
||||
linters:
|
||||
name: linters
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: returntocorp/semgrep-action@v1
|
||||
env:
|
||||
SEMGREP_TIMEOUT: 120
|
||||
with:
|
||||
config: >-
|
||||
r/python.lang.correctness
|
||||
.github/helper/semgrep_rules
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Install and Run Pre-commit
|
||||
uses: pre-commit/action@v2.0.0
|
||||
85
.github/workflows/patch.yml
vendored
85
.github/workflows/patch.yml
vendored
@@ -1,85 +0,0 @@
|
||||
name: Patch
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.js'
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
name: Patch Test
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: YES
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.6
|
||||
|
||||
- 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
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
|
||||
- name: Run Patch Tests
|
||||
run: |
|
||||
cd ~/frappe-bench/
|
||||
wget https://erpnext.com/files/v10-erpnext.sql.gz
|
||||
bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz
|
||||
bench --site test_site migrate
|
||||
126
.github/workflows/server-tests.yml
vendored
126
.github/workflows/server-tests.yml
vendored
@@ -1,126 +0,0 @@
|
||||
name: Server
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.js'
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
paths-ignore:
|
||||
- '**.js'
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
container: [1, 2, 3]
|
||||
|
||||
name: Python Unit Tests
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: YES
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.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
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install
|
||||
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 --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 }}
|
||||
112
.github/workflows/ui-tests.yml
vendored
112
.github/workflows/ui-tests.yml
vendored
@@ -1,112 +0,0 @@
|
||||
name: UI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
name: UI Tests (Cypress)
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: YES
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
check-latest: true
|
||||
|
||||
- name: Add to Hosts
|
||||
run: |
|
||||
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Cache cypress binary
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache
|
||||
key: ${{ runner.os }}-cypress-
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cypress-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Install
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
env:
|
||||
DB: mariadb
|
||||
TYPE: ui
|
||||
|
||||
- name: Site Setup
|
||||
run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests
|
||||
|
||||
- name: cypress pre-requisites
|
||||
run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile
|
||||
|
||||
|
||||
- name: Build Assets
|
||||
run: cd ~/frappe-bench/ && bench build
|
||||
env:
|
||||
CI: Yes
|
||||
|
||||
- name: UI Tests
|
||||
run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd
|
||||
|
||||
- name: Show bench console if tests failed
|
||||
if: ${{ failure() }}
|
||||
run: cat ~/frappe-bench/bench_run_logs.txt
|
||||
58
.mergify.yml
58
.mergify.yml
@@ -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 }}"
|
||||
@@ -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
8
.snyk
Normal 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'
|
||||
69
.travis.yml
Normal file
69
.travis.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
language: python
|
||||
dist: trusty
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
cache:
|
||||
- pip
|
||||
|
||||
addons:
|
||||
hosts: test_site
|
||||
mariadb: 10.3
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: "Python 3.6 Server Side Test"
|
||||
python: 3.6
|
||||
script: bench --site test_site run-tests --app erpnext --coverage
|
||||
|
||||
- name: "Python 3.6 Patch Test"
|
||||
python: 3.6
|
||||
before_script:
|
||||
- wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
|
||||
- bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
|
||||
script: bench --site test_site migrate
|
||||
|
||||
install:
|
||||
- cd ~
|
||||
- nvm install 10
|
||||
|
||||
- pip install frappe-bench
|
||||
|
||||
- git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
|
||||
- bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench
|
||||
|
||||
- mkdir ~/frappe-bench/sites/test_site
|
||||
- cp -r $TRAVIS_BUILD_DIR/.travis/site_config.json ~/frappe-bench/sites/test_site/
|
||||
|
||||
- mysql -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
|
||||
- mysql -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
|
||||
|
||||
- mysql -u root -e "CREATE DATABASE test_frappe"
|
||||
- mysql -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
|
||||
- mysql -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
|
||||
|
||||
- mysql -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
|
||||
- mysql -u root -e "FLUSH PRIVILEGES"
|
||||
|
||||
- wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
|
||||
- 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
|
||||
|
||||
- sed -i 's/watch:/# watch:/g' Procfile
|
||||
- sed -i 's/schedule:/# schedule:/g' Procfile
|
||||
- sed -i 's/socketio:/# socketio:/g' Procfile
|
||||
- sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
|
||||
|
||||
- bench get-app erpnext $TRAVIS_BUILD_DIR
|
||||
- bench start &
|
||||
- bench --site test_site reinstall --yes
|
||||
|
||||
after_script:
|
||||
- pip install coverage==4.5.4
|
||||
- pip install python-coveralls
|
||||
- coveralls -b apps/erpnext -d ../../sites/.coverage
|
||||
@@ -1,6 +1,4 @@
|
||||
{
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 3306,
|
||||
"db_name": "test_frappe",
|
||||
"db_password": "test_frappe",
|
||||
"auto_email_id": "test@example.com",
|
||||
43
CODEOWNERS
43
CODEOWNERS
@@ -3,33 +3,16 @@
|
||||
# These owners will be the default owners for everything in
|
||||
# the repo. Unless a later match takes precedence,
|
||||
|
||||
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007
|
||||
erpnext/assets/ @nextchamp-saqib @deepeshgarg007
|
||||
erpnext/erpnext_integrations/ @nextchamp-saqib
|
||||
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
|
||||
erpnext/regional @nextchamp-saqib @deepeshgarg007
|
||||
erpnext/selling @nextchamp-saqib @deepeshgarg007
|
||||
erpnext/support/ @nextchamp-saqib @deepeshgarg007
|
||||
pos* @nextchamp-saqib
|
||||
|
||||
erpnext/buying/ @marination @rohitwaghchaure @ankush
|
||||
erpnext/e_commerce/ @marination
|
||||
erpnext/maintenance/ @marination @rohitwaghchaure
|
||||
erpnext/manufacturing/ @marination @rohitwaghchaure @ankush
|
||||
erpnext/portal/ @marination
|
||||
erpnext/quality_management/ @marination @rohitwaghchaure
|
||||
erpnext/shopping_cart/ @marination
|
||||
erpnext/stock/ @marination @rohitwaghchaure @ankush
|
||||
|
||||
erpnext/crm/ @ruchamahabal @pateljannat
|
||||
erpnext/education/ @ruchamahabal @pateljannat
|
||||
erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand
|
||||
erpnext/hr/ @ruchamahabal @pateljannat
|
||||
erpnext/non_profit/ @ruchamahabal
|
||||
erpnext/payroll @ruchamahabal @pateljannat
|
||||
erpnext/projects/ @ruchamahabal @pateljannat
|
||||
|
||||
erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
|
||||
|
||||
.github/ @surajshetty3416 @ankush
|
||||
requirements.txt @gavindsouza
|
||||
manufacturing/ @rohitwaghchaure @marination
|
||||
accounts/ @deepeshgarg007 @nextchamp-saqib
|
||||
loan_management/ @deepeshgarg007 @rohitwaghchaure
|
||||
pos* @nextchamp-saqib @rohitwaghchaure
|
||||
assets/ @nextchamp-saqib @deepeshgarg007
|
||||
stock/ @marination @rohitwaghchaure
|
||||
buying/ @marination @deepeshgarg007
|
||||
hr/ @Anurag810 @rohitwaghchaure
|
||||
projects/ @hrwX @nextchamp-saqib
|
||||
support/ @hrwX @marination
|
||||
healthcare/ @ruchamahabal @marination
|
||||
erpnext_integrations/ @Mangesh-Khairnar @nextchamp-saqib
|
||||
requirements.txt @gavindsouza
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<p>ERP made simple</p>
|
||||
</p>
|
||||
|
||||
[](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml)
|
||||
[](https://travis-ci.com/frappe/erpnext)
|
||||
[](https://www.codetriage.com/frappe/erpnext)
|
||||
[](https://coveralls.io/github/frappe/erpnext?branch=develop)
|
||||
|
||||
@@ -39,10 +39,6 @@ ERPNext is built on the [Frappe Framework](https://github.com/frappe/frappe), a
|
||||
|
||||
---
|
||||
|
||||
### Containerized Installation
|
||||
|
||||
Use docker to deploy ERPNext in production or for development of [Frappe](https://github.com/frappe/frappe) apps. See https://github.com/frappe/frappe_docker for more details.
|
||||
|
||||
### Full Install
|
||||
|
||||
The Easy Way: our install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench for more details.
|
||||
|
||||
11
cypress.json
11
cypress.json
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"baseUrl": "http://test_site:8000",
|
||||
"projectId": "da59y9",
|
||||
"adminPassword": "admin",
|
||||
"defaultCommandTimeout": 20000,
|
||||
"pageLoadTimeout": 15000,
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 2
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
context('Customer', () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
});
|
||||
it('Check Customer Group', () => {
|
||||
cy.visit(`app/customer/`);
|
||||
cy.get('.primary-action').click();
|
||||
cy.wait(500);
|
||||
cy.get('.custom-actions > .btn').click();
|
||||
cy.get_field('customer_group', 'Link').should('have.value', 'All Customer Groups');
|
||||
});
|
||||
});
|
||||
@@ -1,116 +0,0 @@
|
||||
context('Organizational Chart', () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
});
|
||||
|
||||
it('navigates to org chart', () => {
|
||||
cy.visit('/app');
|
||||
cy.visit('/app/organizational-chart');
|
||||
cy.url().should('include', '/organizational-chart');
|
||||
|
||||
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
||||
return cy.request({
|
||||
url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-Frappe-CSRF-Token': csrf_token
|
||||
},
|
||||
timeout: 60000
|
||||
}).then(res => {
|
||||
expect(res.status).eq(200);
|
||||
cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
|
||||
cy.get('@input')
|
||||
.clear({ force: true })
|
||||
.type('Test Org Chart{downarrow}{enter}', { force: true })
|
||||
.blur({ force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders root nodes and loads children for the first expandable node', () => {
|
||||
// check rendered root nodes and the node name, title, connections
|
||||
cy.get('.hierarchy').find('.root-level ul.node-children').children()
|
||||
.should('have.length', 2)
|
||||
.first()
|
||||
.as('first-child');
|
||||
|
||||
cy.get('@first-child').get('.node-name').contains('Test Employee 1');
|
||||
cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO');
|
||||
cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2 Connections');
|
||||
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
// children of 1st root visible
|
||||
cy.get(`div[data-parent="${employee_records.message[0]}"]`).as('child-node');
|
||||
cy.get('@child-node')
|
||||
.should('have.length', 1)
|
||||
.should('be.visible');
|
||||
cy.get('@child-node').get('.node-name').contains('Test Employee 3');
|
||||
|
||||
// connectors between first root node and immediate child
|
||||
cy.get(`path[data-parent="${employee_records.message[0]}"]`)
|
||||
.should('be.visible')
|
||||
.invoke('attr', 'data-child')
|
||||
.should('equal', employee_records.message[2]);
|
||||
});
|
||||
});
|
||||
|
||||
it('hides active nodes children and connectors on expanding sibling node', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
// click sibling
|
||||
cy.get(`#${employee_records.message[1]}`)
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// child nodes and connectors hidden
|
||||
cy.get(`[data-parent="${employee_records.message[0]}"]`).should('not.be.visible');
|
||||
cy.get(`path[data-parent="${employee_records.message[0]}"]`).should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('collapses previous level nodes and refreshes connectors on expanding child node', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
// click child node
|
||||
cy.get(`#${employee_records.message[3]}`)
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// previous level nodes: parent should be on active-path; other nodes should be collapsed
|
||||
cy.get(`#${employee_records.message[0]}`).should('have.class', 'collapsed');
|
||||
cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path');
|
||||
|
||||
// previous level connectors refreshed
|
||||
cy.get(`path[data-parent="${employee_records.message[1]}"]`)
|
||||
.should('have.class', 'collapsed-connector');
|
||||
|
||||
// child node's children and connectors rendered
|
||||
cy.get(`[data-parent="${employee_records.message[3]}"]`).should('be.visible');
|
||||
cy.get(`path[data-parent="${employee_records.message[3]}"]`).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('expands previous level nodes', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
cy.get(`#${employee_records.message[0]}`)
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
cy.get(`[data-parent="${employee_records.message[0]}"]`)
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('ul.hierarchy').children().should('have.length', 2);
|
||||
cy.get(`#connectors`).children().should('have.length', 1);
|
||||
});
|
||||
});
|
||||
|
||||
it('edit node navigates to employee master', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node')
|
||||
.click();
|
||||
|
||||
cy.url().should('include', `/employee/${employee_records.message[0]}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,195 +0,0 @@
|
||||
context('Organizational Chart Mobile', () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
});
|
||||
|
||||
it('navigates to org chart', () => {
|
||||
cy.viewport(375, 667);
|
||||
cy.visit('/app');
|
||||
cy.visit('/app/organizational-chart');
|
||||
cy.url().should('include', '/organizational-chart');
|
||||
|
||||
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
||||
return cy.request({
|
||||
url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-Frappe-CSRF-Token': csrf_token
|
||||
},
|
||||
timeout: 60000
|
||||
}).then(res => {
|
||||
expect(res.status).eq(200);
|
||||
cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
|
||||
cy.get('@input')
|
||||
.clear({ force: true })
|
||||
.type('Test Org Chart{downarrow}{enter}', { force: true })
|
||||
.blur({ force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders root nodes', () => {
|
||||
// check rendered root nodes and the node name, title, connections
|
||||
cy.get('.hierarchy-mobile').find('.root-level').children()
|
||||
.should('have.length', 2)
|
||||
.first()
|
||||
.as('first-child');
|
||||
|
||||
cy.get('@first-child').get('.node-name').contains('Test Employee 1');
|
||||
cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO');
|
||||
cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2');
|
||||
});
|
||||
|
||||
it('expands root node', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
cy.get(`#${employee_records.message[1]}`)
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// other root node removed
|
||||
cy.get(`#${employee_records.message[0]}`).should('not.exist');
|
||||
|
||||
// children of active root node
|
||||
cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children()
|
||||
.should('have.length', 2);
|
||||
|
||||
cy.get(`div[data-parent="${employee_records.message[1]}"]`).first().as('child-node');
|
||||
cy.get('@child-node').should('be.visible');
|
||||
|
||||
cy.get('@child-node')
|
||||
.get('.node-name')
|
||||
.contains('Test Employee 4');
|
||||
|
||||
// connectors between root node and immediate children
|
||||
cy.get(`path[data-parent="${employee_records.message[1]}"]`).as('connectors');
|
||||
cy.get('@connectors')
|
||||
.should('have.length', 2)
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('@connectors')
|
||||
.first()
|
||||
.invoke('attr', 'data-child')
|
||||
.should('eq', employee_records.message[3]);
|
||||
});
|
||||
});
|
||||
|
||||
it('expands child node', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
cy.get(`#${employee_records.message[3]}`)
|
||||
.click()
|
||||
.should('have.class', 'active')
|
||||
.as('expanded_node');
|
||||
|
||||
// 2 levels on screen; 1 on active path; 1 collapsed
|
||||
cy.get('.hierarchy-mobile').children().should('have.length', 2);
|
||||
cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path');
|
||||
|
||||
// children of expanded node visible
|
||||
cy.get('@expanded_node')
|
||||
.next()
|
||||
.should('have.class', 'node-children')
|
||||
.as('node-children');
|
||||
|
||||
cy.get('@node-children').children().should('have.length', 1);
|
||||
cy.get('@node-children')
|
||||
.first()
|
||||
.get('.node-card')
|
||||
.should('have.class', 'active-child')
|
||||
.contains('Test Employee 7');
|
||||
|
||||
// orphan connectors removed
|
||||
cy.get(`#connectors`).children().should('have.length', 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders sibling group', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
// sibling group visible for parent
|
||||
cy.get(`#${employee_records.message[1]}`)
|
||||
.next()
|
||||
.as('sibling_group');
|
||||
|
||||
cy.get('@sibling_group')
|
||||
.should('have.attr', 'data-parent', 'undefined')
|
||||
.should('have.class', 'node-group')
|
||||
.and('have.class', 'collapsed');
|
||||
|
||||
cy.get('@sibling_group').get('.avatar-group').children().as('siblings');
|
||||
cy.get('@siblings').should('have.length', 1);
|
||||
cy.get('@siblings')
|
||||
.first()
|
||||
.should('have.attr', 'title', 'Test Employee 1');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('expands previous level nodes', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
cy.get(`#${employee_records.message[6]}`)
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// clicking on previous level node should remove all the nodes ahead
|
||||
// and expand that node
|
||||
cy.get(`#${employee_records.message[3]}`).click();
|
||||
cy.get(`#${employee_records.message[3]}`)
|
||||
.should('have.class', 'active')
|
||||
.should('not.have.class', 'active-path');
|
||||
|
||||
cy.get(`#${employee_records.message[6]}`).should('have.class', 'active-child');
|
||||
cy.get('.hierarchy-mobile').children().should('have.length', 2);
|
||||
cy.get(`#connectors`).children().should('have.length', 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('expands sibling group', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
// sibling group visible for parent
|
||||
cy.get(`#${employee_records.message[6]}`).click();
|
||||
|
||||
cy.get(`#${employee_records.message[3]}`)
|
||||
.next()
|
||||
.click();
|
||||
|
||||
// siblings of parent should be visible
|
||||
cy.get('.hierarchy-mobile').prev().as('sibling_group');
|
||||
cy.get('@sibling_group')
|
||||
.should('exist')
|
||||
.should('have.class', 'sibling-group')
|
||||
.should('not.have.class', 'collapsed');
|
||||
|
||||
cy.get(`#${employee_records.message[1]}`)
|
||||
.should('be.visible')
|
||||
.should('have.class', 'active');
|
||||
|
||||
cy.get(`[data-parent="${employee_records.message[1]}"]`)
|
||||
.should('be.visible')
|
||||
.should('have.length', 2)
|
||||
.should('have.class', 'active-child');
|
||||
});
|
||||
});
|
||||
|
||||
it('goes to the respective level after clicking on non-collapsed sibling group', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(() => {
|
||||
// click on non-collapsed sibling group
|
||||
cy.get('.hierarchy-mobile')
|
||||
.prev()
|
||||
.click();
|
||||
|
||||
// should take you to that level
|
||||
cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('edit node navigates to employee master', () => {
|
||||
cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
|
||||
cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node')
|
||||
.click();
|
||||
|
||||
cy.url().should('include', `/employee/${employee_records.message[0]}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
module.exports = () => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... });
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... });
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... });
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... });
|
||||
|
||||
const slug = (name) => name.toLowerCase().replace(" ", "-");
|
||||
|
||||
Cypress.Commands.add("go_to_doc", (doctype, name) => {
|
||||
cy.visit(`/app/${slug(doctype)}/${encodeURIComponent(name)}`);
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
import '../../../frappe/cypress/support/commands' // eslint-disable-line
|
||||
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
Cypress.Cookies.defaults({
|
||||
preserve: 'sid'
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"baseUrl": "../node_modules",
|
||||
"types": [
|
||||
"cypress"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.*"
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": ["stylelint-config-recommended"],
|
||||
"plugins": ["stylelint-scss"],
|
||||
"rules": {
|
||||
"at-rule-no-unknown": null,
|
||||
"scss/at-rule-no-unknown": true,
|
||||
"no-descending-specificity": null
|
||||
}
|
||||
}
|
||||
@@ -1,13 +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.13.0'
|
||||
__version__ = '13.0.0-beta.14'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -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 = [
|
||||
@@ -50,8 +33,6 @@ def get_shipping_address(company, address = None):
|
||||
if address and frappe.db.get_value('Dynamic Link',
|
||||
{'parent': address, 'link_name': company}):
|
||||
filters.append(["Address", "name", "=", address])
|
||||
if not address:
|
||||
filters.append(["Address", "is_shipping_address", "=", 1])
|
||||
|
||||
address = frappe.get_all("Address", filters=filters, fields=fields) or {}
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = {
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -2,15 +2,14 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
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,
|
||||
|
||||
@@ -2,26 +2,11 @@ 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 '''
|
||||
@@ -56,7 +41,7 @@ def build_conditions(process_type, account, company):
|
||||
if account:
|
||||
conditions += "AND %s='%s'"%(deferred_account, account)
|
||||
elif company:
|
||||
conditions += f"AND p.company = {frappe.db.escape(company)}"
|
||||
conditions += "AND p.company='%s'"%(company)
|
||||
|
||||
return conditions
|
||||
|
||||
@@ -278,9 +263,6 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
|
||||
total_days, total_booking_days, account_currency)
|
||||
|
||||
if not amount:
|
||||
return
|
||||
|
||||
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)
|
||||
@@ -316,21 +298,17 @@ def process_deferred_accounting(posting_date=None):
|
||||
start_date = add_months(today(), -1)
|
||||
end_date = add_days(today(), -1)
|
||||
|
||||
companies = frappe.get_all('Company')
|
||||
for record_type in ('Income', 'Expense'):
|
||||
doc = frappe.get_doc(dict(
|
||||
doctype='Process Deferred Accounting',
|
||||
posting_date=posting_date,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
type=record_type
|
||||
))
|
||||
|
||||
for company in companies:
|
||||
for record_type in ('Income', 'Expense'):
|
||||
doc = frappe.get_doc(dict(
|
||||
doctype='Process Deferred Accounting',
|
||||
company=company.name,
|
||||
posting_date=posting_date,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
type=record_type
|
||||
))
|
||||
|
||||
doc.insert()
|
||||
doc.submit()
|
||||
doc.insert()
|
||||
doc.submit()
|
||||
|
||||
def make_gl_entries(doc, credit_account, debit_account, against,
|
||||
amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
|
||||
@@ -374,21 +352,20 @@ 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:
|
||||
raise e
|
||||
else:
|
||||
frappe.db.rollback()
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(message=traceback)
|
||||
except:
|
||||
frappe.db.rollback()
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(message=traceback)
|
||||
|
||||
frappe.flags.deferred_accounting_error = True
|
||||
frappe.flags.deferred_accounting_error = True
|
||||
|
||||
def send_mail(deferred_process):
|
||||
title = _("Error while processing deferred accounting for {0}").format(deferred_process)
|
||||
link = get_link_to_form('Process Deferred Accounting', deferred_process)
|
||||
content = _("Deferred accounting failed for some invoices:") + "\n"
|
||||
content += _("Please check Process Deferred Accounting {0} and submit manually after resolving errors.").format(link)
|
||||
title = _("Error while processing deferred accounting for {0}".format(deferred_process))
|
||||
content = _("""
|
||||
Deferred accounting failed for some invoices:
|
||||
Please check Process Deferred Accounting {0}
|
||||
and submit manually after resolving errors
|
||||
""").format(get_link_to_form('Process Deferred Accounting', deferred_process))
|
||||
sendmail_to_system_managers(title, content)
|
||||
|
||||
def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
||||
@@ -448,7 +425,7 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
||||
|
||||
if submit:
|
||||
journal_entry.submit()
|
||||
except Exception:
|
||||
except:
|
||||
frappe.db.rollback()
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(message=traceback)
|
||||
@@ -468,3 +445,5 @@ def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
||||
return debit_account
|
||||
else:
|
||||
return credit_account
|
||||
|
||||
|
||||
|
||||
161
erpnext/accounts/desk_page/accounting/accounting.json
Normal file
161
erpnext/accounts/desk_page/accounting/accounting.json
Normal file
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"cards": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Accounting Masters",
|
||||
"links": "[\n {\n \"description\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounts Settings\",\n \"name\": \"Accounts Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Financial / accounting year.\",\n \"label\": \"Fiscal Year\",\n \"name\": \"Fiscal Year\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Finance Book\",\n \"name\": \"Finance Book\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Period\",\n \"name\": \"Accounting Period\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Terms based on conditions\",\n \"label\": \"Payment Term\",\n \"name\": \"Payment Term\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "General Ledger",
|
||||
"links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make journal entries from a template.\",\n \"label\": \"Journal Entry Template\",\n \"name\": \"Journal Entry Template\",\n \"type\": \"doctype\"\n },\n \n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Accounts Receivable",
|
||||
"links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Accounts Payable",
|
||||
"links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Reports",
|
||||
"links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"DATEV Export\",\n \"name\": \"DATEV\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Financial Statements",
|
||||
"links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance\",\n \"name\": \"Trial Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profit and Loss Statement\",\n \"name\": \"Profit and Loss Statement\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Balance Sheet\",\n \"name\": \"Balance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Cash Flow\",\n \"name\": \"Cash Flow\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Consolidated Financial Statement\",\n \"name\": \"Consolidated Financial Statement\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Multi Currency",
|
||||
"links": "[\n {\n \"description\": \"Enable / disable currencies.\",\n \"label\": \"Currency\",\n \"name\": \"Currency\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Currency exchange rate master.\",\n \"label\": \"Currency Exchange\",\n \"name\": \"Currency Exchange\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Exchange Rate Revaluation master.\",\n \"label\": \"Exchange Rate Revaluation\",\n \"name\": \"Exchange Rate Revaluation\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Settings",
|
||||
"links": "[\n {\n \"description\": \"Setup Gateway accounts.\",\n \"label\": \"Payment Gateway Account\",\n \"name\": \"Payment Gateway Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"e.g. Bank, Cash, Credit Card\",\n \"label\": \"Mode of Payment\",\n \"name\": \"Mode of Payment\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Bank Statement",
|
||||
"links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Subscription Management",
|
||||
"links": "[\n {\n \"label\": \"Subscription Plan\",\n \"name\": \"Subscription Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription\",\n \"name\": \"Subscription\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription Settings\",\n \"name\": \"Subscription Settings\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Goods and Services Tax (GST India)",
|
||||
"links": "[\n {\n \"label\": \"GST Settings\",\n \"name\": \"GST Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"GST HSN Code\",\n \"name\": \"GST HSN Code\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-1\",\n \"name\": \"GSTR-1\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-2\",\n \"name\": \"GSTR-2\",\n \"type\": \"report\"\n },\n {\n \"label\": \"GSTR 3B Report\",\n \"name\": \"GSTR 3B Report\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Sales Register\",\n \"name\": \"GST Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Purchase Register\",\n \"name\": \"GST Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Sales Register\",\n \"name\": \"GST Itemised Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Purchase Register\",\n \"name\": \"GST Itemised Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"country\": \"India\",\n \"description\": \"C-Form records\",\n \"label\": \"C-Form\",\n \"name\": \"C-Form\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"India\",\n \"label\": \"Lower Deduction Certificate\",\n \"name\": \"Lower Deduction Certificate\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Share Management",
|
||||
"links": "[\n {\n \"description\": \"List of available Shareholders with folio numbers\",\n \"label\": \"Shareholder\",\n \"name\": \"Shareholder\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of all share transactions\",\n \"label\": \"Share Transfer\",\n \"name\": \"Share Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Ledger\",\n \"name\": \"Share Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Balance\",\n \"name\": \"Share Balance\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Cost Center and Budgeting",
|
||||
"links": "[\n {\n \"description\": \"Tree of financial Cost Centers.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Cost Centers\",\n \"name\": \"Cost Center\",\n \"route\": \"#Tree/Cost Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define budget for a financial year.\",\n \"label\": \"Budget\",\n \"name\": \"Budget\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Cost Center\"\n ],\n \"doctype\": \"Cost Center\",\n \"is_query_report\": true,\n \"label\": \"Budget Variance Report\",\n \"name\": \"Budget Variance Report\",\n \"type\": \"report\"\n },\n {\n \"description\": \"Seasonality for setting budgets, targets etc.\",\n \"label\": \"Monthly Distribution\",\n \"name\": \"Monthly Distribution\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Opening and Closing",
|
||||
"links": "[\n {\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Close Balance Sheet and book Profit or Loss.\",\n \"label\": \"Period Closing Voucher\",\n \"name\": \"Period Closing Voucher\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Taxes",
|
||||
"links": "[\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for item tax rates.\",\n \"label\": \"Item Tax Template\",\n \"name\": \"Item Tax Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Category for overriding tax rates.\",\n \"label\": \"Tax Category\",\n \"name\": \"Tax Category\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Rule for transactions.\",\n \"label\": \"Tax Rule\",\n \"name\": \"Tax Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Withholding rates to be applied on transactions.\",\n \"label\": \"Tax Withholding Category\",\n \"name\": \"Tax Withholding Category\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Profitability",
|
||||
"links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Value-Added Tax (VAT UAE)",
|
||||
"links": "[\n {\n \"country\": \"United Arab Emirates\",\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"United Arab Emirates\",\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n\n]"
|
||||
}
|
||||
],
|
||||
"category": "Modules",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "Profit and Loss",
|
||||
"label": "Profit and Loss"
|
||||
}
|
||||
],
|
||||
"creation": "2020-03-02 15:41:59.515192",
|
||||
"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": "Accounting",
|
||||
"modified": "2020-11-11 18:35:11.542909",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting",
|
||||
"onboarding": "Accounts",
|
||||
"owner": "Administrator",
|
||||
"pin_to_bottom": 0,
|
||||
"pin_to_top": 0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"label": "Chart Of Accounts",
|
||||
"link_to": "Account",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Sales Invoice",
|
||||
"link_to": "Sales Invoice",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Purchase Invoice",
|
||||
"link_to": "Purchase Invoice",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Journal Entry",
|
||||
"link_to": "Journal Entry",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Payment Entry",
|
||||
"link_to": "Payment Entry",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Accounts Receivable",
|
||||
"link_to": "Accounts Receivable",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "General Ledger",
|
||||
"link_to": "General Ledger",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Trial Balance",
|
||||
"link_to": "Trial Balance",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Dashboard",
|
||||
"link_to": "Accounts",
|
||||
"type": "Dashboard"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -74,7 +74,7 @@ frappe.ui.form.on('Account', {
|
||||
});
|
||||
} else if (cint(frm.doc.is_group) == 0
|
||||
&& frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
|
||||
frm.add_custom_button(__('Ledger'), function () {
|
||||
cur_frm.add_custom_button(__('Ledger'), function () {
|
||||
frappe.route_options = {
|
||||
"account": frm.doc.name,
|
||||
"from_date": frappe.sys_defaults.year_start_date,
|
||||
|
||||
@@ -2,22 +2,18 @@
|
||||
# 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
|
||||
|
||||
class Account(NestedSet):
|
||||
nsm_parent_field = 'parent_account'
|
||||
def on_update(self):
|
||||
if frappe.local.flags.ignore_update_nsm:
|
||||
if frappe.local.flags.ignore_on_update:
|
||||
return
|
||||
else:
|
||||
super(Account, self).on_update()
|
||||
@@ -198,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]
|
||||
})
|
||||
|
||||
@@ -209,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))
|
||||
@@ -217,7 +214,6 @@ class Account(NestedSet):
|
||||
if parent_value_changed:
|
||||
doc.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def convert_group_to_ledger(self):
|
||||
if self.check_if_child_exists():
|
||||
throw(_("Account with child nodes cannot be converted to ledger"))
|
||||
@@ -228,12 +224,11 @@ class Account(NestedSet):
|
||||
self.save()
|
||||
return 1
|
||||
|
||||
@frappe.whitelist()
|
||||
def convert_ledger_to_group(self):
|
||||
if self.check_gle_exists():
|
||||
throw(_("Account with existing transaction can not be converted to group."))
|
||||
elif self.account_type and not self.flags.exclude_account_type_check:
|
||||
throw(_("Cannot convert to Group because Account Type is selected."))
|
||||
throw(_("Cannot covert to Group because Account Type is selected."))
|
||||
else:
|
||||
self.is_group = 1
|
||||
self.save()
|
||||
|
||||
@@ -45,49 +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) {
|
||||
$('<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:[
|
||||
{
|
||||
@@ -163,7 +120,25 @@ frappe.treeview_settings["Account"] = {
|
||||
} else {
|
||||
treeview.new_node();
|
||||
}
|
||||
}, "add");
|
||||
}, "octicon octicon-plus");
|
||||
},
|
||||
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 text-muted small">'
|
||||
+ (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: [
|
||||
{
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
# 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 = []
|
||||
@@ -22,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()
|
||||
@@ -35,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,
|
||||
@@ -60,10 +57,10 @@ def create_charts(company, chart_template=None, existing_company=None, custom_ch
|
||||
|
||||
# Rebuild NestedSet HSM tree for Account Doctype
|
||||
# after all accounts are already inserted.
|
||||
frappe.local.flags.ignore_update_nsm = True
|
||||
frappe.local.flags.ignore_on_update = True
|
||||
_import_accounts(chart, None, None, root_account=True)
|
||||
rebuild_tree("Account", "parent_account")
|
||||
frappe.local.flags.ignore_update_nsm = False
|
||||
frappe.local.flags.ignore_on_update = False
|
||||
|
||||
def add_suffix_if_duplicate(account_name, account_number, accounts):
|
||||
if account_number:
|
||||
@@ -94,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",)
|
||||
@@ -213,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)
|
||||
|
||||
@@ -226,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) \
|
||||
|
||||
@@ -6,13 +6,12 @@ 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"
|
||||
|
||||
@@ -63,21 +63,17 @@
|
||||
"Gewinnermittlung \u00a74/3 nicht Ergebniswirksam": {
|
||||
"account_number": "1371"
|
||||
},
|
||||
"Abziehbare Vorsteuer": {
|
||||
"account_type": "Tax",
|
||||
"is_group": 1,
|
||||
"Abziehbare Vorsteuer 7%": {
|
||||
"account_number": "1571"
|
||||
},
|
||||
"Abziehbare Vorsteuer 19%": {
|
||||
"account_number": "1576"
|
||||
},
|
||||
"Abziehbare Vorsteuer nach \u00a713b UStG 19%": {
|
||||
"account_number": "1577"
|
||||
},
|
||||
"Leistungen \u00a713b UStG 19% Vorsteuer, 19% Umsatzsteuer": {
|
||||
"account_number": "3120"
|
||||
}
|
||||
"Abziehbare VSt. 7%": {
|
||||
"account_number": "1571"
|
||||
},
|
||||
"Abziehbare VSt. 19%": {
|
||||
"account_number": "1576"
|
||||
},
|
||||
"Abziehbare VStr. nach \u00a713b UStG 19%": {
|
||||
"account_number": "1577"
|
||||
},
|
||||
"Leistungen \u00a713b UStG 19% Vorsteuer, 19% Umsatzsteuer": {
|
||||
"account_number": "3120"
|
||||
}
|
||||
},
|
||||
"III. Wertpapiere": {
|
||||
@@ -200,7 +196,6 @@
|
||||
},
|
||||
"Umsatzsteuer": {
|
||||
"is_group": 1,
|
||||
"account_type": "Tax",
|
||||
"Umsatzsteuer 7%": {
|
||||
"account_number": "1771"
|
||||
},
|
||||
|
||||
@@ -292,21 +292,18 @@
|
||||
"Umsatzsteuerforderungen fr\u00fchere Jahre": {}
|
||||
},
|
||||
"Sonstige Verm\u00f6gensgegenst\u00e4nde oder sonstige Verbindlichkeiten": {
|
||||
"Abziehbare Vorsteuer": {
|
||||
"account_type": "Tax",
|
||||
"is_group": 1,
|
||||
"Abziehbare Vorsteuer 16%": {},
|
||||
"Abziehbare Vorsteuer 19%": {},
|
||||
"Abziehbare Vorsteuer 7%": {},
|
||||
"Abziehbare Vorsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Unsatzsteuerlager": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 16%": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 19%": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb von Neufahrzeugen von Lieferanten ohne Ust-Identifikationsnummer": {},
|
||||
"Abziehbare Vorsteuer nach \u00a7 13b UStG ": {},
|
||||
"Abziehbare Vorsteuer nach \u00a7 13b UStG 16%": {},
|
||||
"Abziehbare Vorsteuer nach \u00a7 13b UStG 19%": {}
|
||||
},
|
||||
"Abziehbare Vorsteuer": {},
|
||||
"Abziehbare Vorsteuer 16%": {},
|
||||
"Abziehbare Vorsteuer 19%": {},
|
||||
"Abziehbare Vorsteuer 7%": {},
|
||||
"Abziehbare Vorsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Unsatzsteuerlager": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 16%": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 19%": {},
|
||||
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb von Neufahrzeugen von Lieferanten ohne Ust-Identifikationsnummer": {},
|
||||
"Abziehbare Vorsteuer nach \u00a7 13b UStG ": {},
|
||||
"Abziehbare Vorsteuer nach \u00a7 13b UStG 16%": {},
|
||||
"Abziehbare Vorsteuer nach \u00a7 13b UStG 19%": {},
|
||||
"Aufl\u00f6sung Vorsteuer aus Vorjahr \u00a7 4/3 EStG": {},
|
||||
"Aufzuteilende Vorsteuer": {},
|
||||
"Aufzuteilende Vorsteuer 16%": {},
|
||||
@@ -676,26 +673,23 @@
|
||||
"Sonstige Verrechnungskonten (Interimskonto)": {
|
||||
"account_type": "Stock Received But Not Billed"
|
||||
},
|
||||
"Umsatzsteuer": {
|
||||
"account_type": "Tax",
|
||||
"is_group": 1,
|
||||
"Umsatzsteuer 16%": {},
|
||||
"Umsatzsteuer 19%": {},
|
||||
"Umsatzsteuer 7%": {},
|
||||
"Umsatzsteuer Vorjahr": {},
|
||||
"Umsatzsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Umsatzsteuerlager": {},
|
||||
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen": {},
|
||||
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen 19%": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 16%": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 19%": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ohne Vorsteuerabzug": {},
|
||||
"Umsatzsteuer fr\u00fchere Jahre": {},
|
||||
"Umsatzsteuer laufendes Jahr": {},
|
||||
"Umsatzsteuer nach \u00a713b UStG": {},
|
||||
"Umsatzsteuer nach \u00a713b UStG 16%": {},
|
||||
"Umsatzsteuer nach \u00a713b UStG 19%": {}
|
||||
},
|
||||
"Umsatzsteuer": {},
|
||||
"Umsatzsteuer 16%": {},
|
||||
"Umsatzsteuer 19%": {},
|
||||
"Umsatzsteuer 7%": {},
|
||||
"Umsatzsteuer Vorjahr": {},
|
||||
"Umsatzsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Umsatzsteuerlager": {},
|
||||
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen": {},
|
||||
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen 19%": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 16%": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 19%": {},
|
||||
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ohne Vorsteuerabzug": {},
|
||||
"Umsatzsteuer fr\u00fchere Jahre": {},
|
||||
"Umsatzsteuer laufendes Jahr": {},
|
||||
"Umsatzsteuer nach \u00a713b UStG": {},
|
||||
"Umsatzsteuer nach \u00a713b UStG 16%": {},
|
||||
"Umsatzsteuer nach \u00a713b UStG 19%": {},
|
||||
"Umsatzsteuer- Vorauszahlungen": {},
|
||||
"Umsatzsteuer- Vorauszahlungen 1/11": {},
|
||||
"Verbindlichkeiten aus Lohn- und Kirchensteuer": {}
|
||||
|
||||
@@ -659,7 +659,6 @@
|
||||
},
|
||||
"Abziehbare Vorsteuer (Gruppe)": {
|
||||
"is_group": 1,
|
||||
"account_type": "Tax",
|
||||
"Abziehbare Vorsteuer": {
|
||||
"account_number": "1400"
|
||||
},
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe import _
|
||||
|
||||
|
||||
def get():
|
||||
return {
|
||||
_("Application of Funds (Assets)"): {
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe import _
|
||||
|
||||
|
||||
def get():
|
||||
return {
|
||||
_("Application of Funds (Assets)"): {
|
||||
|
||||
@@ -2,14 +2,10 @@
|
||||
# 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):
|
||||
|
||||
@@ -60,4 +60,4 @@ frappe.ui.form.on('Accounting Dimension Detail', {
|
||||
let row = locals[cdt][cdn];
|
||||
row.reference_document = frm.doc.document_type;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -30,7 +30,6 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Reference Document Type",
|
||||
"options": "DocType",
|
||||
"read_only_depends_on": "eval:!doc.__islocal",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -49,7 +48,7 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2021-02-08 16:37:53.936656",
|
||||
"modified": "2020-03-22 20:34:39.805728",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting Dimension",
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
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):
|
||||
@@ -20,7 +19,7 @@ class AccountingDimension(Document):
|
||||
|
||||
def validate(self):
|
||||
if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
|
||||
'Cost Center', 'Accounting Dimension Detail', 'Company', 'Account') :
|
||||
'Cost Center', 'Accounting Dimension Detail', 'Company') :
|
||||
|
||||
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
||||
frappe.throw(msg)
|
||||
@@ -28,17 +27,7 @@ class AccountingDimension(Document):
|
||||
exists = frappe.db.get_value("Accounting Dimension", {'document_type': self.document_type}, ['name'])
|
||||
|
||||
if exists and self.is_new():
|
||||
frappe.throw(_("Document Type already used as a dimension"))
|
||||
|
||||
if not self.is_new():
|
||||
self.validate_document_type_change()
|
||||
|
||||
def validate_document_type_change(self):
|
||||
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
|
||||
if doctype_before_save != self.document_type:
|
||||
message = _("Cannot change Reference Document Type.")
|
||||
message += _("Please create a new Accounting Dimension if required.")
|
||||
frappe.throw(message)
|
||||
frappe.throw("Document Type already used as a dimension")
|
||||
|
||||
def after_insert(self):
|
||||
if frappe.flags.in_test:
|
||||
@@ -48,9 +37,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:
|
||||
@@ -62,10 +51,8 @@ class AccountingDimension(Document):
|
||||
def on_update(self):
|
||||
frappe.flags.accounting_dimensions = None
|
||||
|
||||
def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
||||
if not doclist:
|
||||
doclist = get_doctypes_with_dimensions()
|
||||
|
||||
def make_dimension_in_accounting_doctypes(doc):
|
||||
doclist = get_doctypes_with_dimensions()
|
||||
doc_count = len(get_accounting_dimensions())
|
||||
count = 0
|
||||
|
||||
@@ -85,13 +72,13 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
||||
"owner": "Administrator"
|
||||
}
|
||||
|
||||
meta = frappe.get_meta(doctype, cached=False)
|
||||
fieldnames = [d.fieldname for d in meta.get("fields")]
|
||||
if doctype == "Budget":
|
||||
add_dimension_to_budget_doctype(df, doc)
|
||||
else:
|
||||
meta = frappe.get_meta(doctype, cached=False)
|
||||
fieldnames = [d.fieldname for d in meta.get("fields")]
|
||||
|
||||
if df['fieldname'] not in fieldnames:
|
||||
if doctype == "Budget":
|
||||
add_dimension_to_budget_doctype(df.copy(), doc)
|
||||
else:
|
||||
if df['fieldname'] not in fieldnames:
|
||||
create_custom_field(doctype, df)
|
||||
|
||||
count += 1
|
||||
@@ -181,7 +168,15 @@ def toggle_disabling(doc):
|
||||
frappe.clear_cache(doctype=doctype)
|
||||
|
||||
def get_doctypes_with_dimensions():
|
||||
return frappe.get_hooks("accounting_dimension_doctypes")
|
||||
doclist = ["GL Entry", "Sales Invoice", "POS Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||
"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
|
||||
"Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||
"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
||||
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
||||
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
||||
"Subscription Plan"]
|
||||
|
||||
return doclist
|
||||
|
||||
def get_accounting_dimensions(as_list=True):
|
||||
if frappe.flags.accounting_dimensions is None:
|
||||
|
||||
@@ -3,14 +3,11 @@
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||
import unittest
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
test_dependencies = ['Cost Center', 'Location', 'Warehouse', 'Department']
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import delete_accounting_dimension
|
||||
|
||||
class TestAccountingDimension(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -115,3 +112,5 @@ def disable_dimension():
|
||||
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
|
||||
dimension2.disabled = 1
|
||||
dimension2.save()
|
||||
|
||||
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AccountingDimensionDetail(Document):
|
||||
pass
|
||||
|
||||
@@ -79,4 +79,4 @@ frappe.ui.form.on('Allowed Dimension', {
|
||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||
frm.refresh_field("dimensions");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -3,12 +3,10 @@
|
||||
# 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()
|
||||
|
||||
@@ -3,19 +3,12 @@
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
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']
|
||||
|
||||
class TestAccountingDimensionFilter(unittest.TestCase):
|
||||
def setUp(self):
|
||||
create_dimension()
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
from frappe import _
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
|
||||
@@ -41,7 +39,6 @@ class AccountingPeriod(Document):
|
||||
frappe.throw(_("Accounting Period overlaps with {0}")
|
||||
.format(existing_accounting_period[0].get("name")), OverlapError)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_doctypes_for_closing(self):
|
||||
docs_for_closing = []
|
||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
|
||||
@@ -58,4 +55,4 @@ class AccountingPeriod(Document):
|
||||
self.append('closed_documents', {
|
||||
"document_type": doctype_for_closing.document_type,
|
||||
"closed": doctype_for_closing.closed
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -3,48 +3,44 @@
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
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']
|
||||
|
||||
class TestAccountingPeriod(unittest.TestCase):
|
||||
def test_overlap(self):
|
||||
ap1 = create_accounting_period(start_date = "2018-04-01",
|
||||
end_date = "2018-06-30", company = "Wind Power LLC")
|
||||
ap1.save()
|
||||
def test_overlap(self):
|
||||
ap1 = create_accounting_period(start_date = "2018-04-01",
|
||||
end_date = "2018-06-30", company = "Wind Power LLC")
|
||||
ap1.save()
|
||||
|
||||
ap2 = create_accounting_period(start_date = "2018-06-30",
|
||||
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
||||
self.assertRaises(OverlapError, ap2.save)
|
||||
ap2 = create_accounting_period(start_date = "2018-06-30",
|
||||
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
||||
self.assertRaises(OverlapError, ap2.save)
|
||||
|
||||
def test_accounting_period(self):
|
||||
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
||||
ap1.save()
|
||||
def test_accounting_period(self):
|
||||
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
||||
ap1.save()
|
||||
|
||||
doc = create_sales_invoice(do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
||||
doc = create_sales_invoice(do_not_submit=1, cost_center = "_Test Company - _TC", warehouse = "Stores - _TC")
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
||||
|
||||
def tearDown(self):
|
||||
for d in frappe.get_all("Accounting Period"):
|
||||
frappe.delete_doc("Accounting Period", d.name)
|
||||
def tearDown(self):
|
||||
for d in frappe.get_all("Accounting Period"):
|
||||
frappe.delete_doc("Accounting Period", d.name)
|
||||
|
||||
def create_accounting_period(**args):
|
||||
args = frappe._dict(args)
|
||||
args = frappe._dict(args)
|
||||
|
||||
accounting_period = frappe.new_doc("Accounting Period")
|
||||
accounting_period.start_date = args.start_date or nowdate()
|
||||
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
||||
accounting_period.company = args.company or "_Test Company"
|
||||
accounting_period.period_name = args.period_name or "_Test_Period_Name_1"
|
||||
accounting_period.append("closed_documents", {
|
||||
"document_type": 'Sales Invoice', "closed": 1
|
||||
})
|
||||
accounting_period = frappe.new_doc("Accounting Period")
|
||||
accounting_period.start_date = args.start_date or nowdate()
|
||||
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
||||
accounting_period.company = args.company or "_Test Company"
|
||||
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
|
||||
accounting_period.append("closed_documents", {
|
||||
"document_type": 'Sales Invoice', "closed": 1
|
||||
})
|
||||
|
||||
return accounting_period
|
||||
return accounting_period
|
||||
@@ -6,46 +6,3 @@ frappe.ui.form.on('Accounts Settings', {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
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.")
|
||||
}
|
||||
];
|
||||
|
||||
@@ -7,33 +7,26 @@
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"accounts_transactions_settings_section",
|
||||
"over_billing_allowance",
|
||||
"role_allowed_to_over_bill",
|
||||
"make_payment_via_journal_entry",
|
||||
"column_break_11",
|
||||
"check_supplier_invoice_uniqueness",
|
||||
"unlink_payment_on_cancellation_of_invoice",
|
||||
"automatically_fetch_payment_terms",
|
||||
"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",
|
||||
"determine_address_tax_category_from",
|
||||
"column_break_19",
|
||||
"add_taxes_from_item_tax_template",
|
||||
"period_closing_settings_section",
|
||||
"auto_accounting_for_stock",
|
||||
"acc_frozen_upto",
|
||||
"frozen_accounts_modifier",
|
||||
"determine_address_tax_category_from",
|
||||
"over_billing_allowance",
|
||||
"role_allowed_to_over_bill",
|
||||
"column_break_4",
|
||||
"credit_controller",
|
||||
"check_supplier_invoice_uniqueness",
|
||||
"make_payment_via_journal_entry",
|
||||
"unlink_payment_on_cancellation_of_invoice",
|
||||
"unlink_advance_payment_on_cancelation_of_order",
|
||||
"book_asset_depreciation_entry_automatically",
|
||||
"add_taxes_from_item_tax_template",
|
||||
"automatically_fetch_payment_terms",
|
||||
"delete_linked_ledger_entries",
|
||||
"deferred_accounting_settings_section",
|
||||
"automatically_process_deferred_accounting_entry",
|
||||
"book_deferred_entries_based_on",
|
||||
"column_break_18",
|
||||
"automatically_process_deferred_accounting_entry",
|
||||
"book_deferred_entries_via_journal_entry",
|
||||
"submit_journal_entries",
|
||||
"print_settings",
|
||||
@@ -47,6 +40,15 @@
|
||||
"use_custom_cash_flow"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "1",
|
||||
"description": "If enabled, the system will post accounting entries for inventory automatically",
|
||||
"fieldname": "auto_accounting_for_stock",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Make Accounting Entry For Every Stock Movement"
|
||||
},
|
||||
{
|
||||
"description": "Accounting entries are frozen up to this date. Nobody can create or modify entries except users with the role specified below",
|
||||
"fieldname": "acc_frozen_upto",
|
||||
@@ -92,7 +94,6 @@
|
||||
"default": "0",
|
||||
"fieldname": "make_payment_via_journal_entry",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Make Payment via Journal Entry"
|
||||
},
|
||||
{
|
||||
@@ -175,7 +176,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 ",
|
||||
@@ -228,54 +229,11 @@
|
||||
"label": "Delete Accounting and Stock Ledger Entries on deletion of Transaction"
|
||||
},
|
||||
{
|
||||
"description": "Users with this role are allowed to over bill above the allowance percentage",
|
||||
"description": "Users with this role are allowed to over bill above the allowance perecentage",
|
||||
"fieldname": "role_allowed_to_over_bill",
|
||||
"fieldtype": "Link",
|
||||
"label": "Role Allowed to Over Bill ",
|
||||
"options": "Role"
|
||||
},
|
||||
{
|
||||
"fieldname": "period_closing_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Period Closing Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "accounts_transactions_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Transactions Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Tax Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_19",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"description": "If enabled, ledger entries will be posted for change amount in POS transactions",
|
||||
"fieldname": "post_change_gl_entries",
|
||||
"fieldtype": "Check",
|
||||
"label": "Create Ledger Entries for Change Amount"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If enabled, additional ledger entries will be made for discounts in a separate Discount Account",
|
||||
"fieldname": "enable_discount_accounting",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Discount Accounting"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_common_party_accounting",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Common Party Accounting"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
@@ -283,7 +241,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-10-11 17:42:36.427699",
|
||||
"modified": "2021-03-11 18:52:05.601996",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
# 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.model.document import Document
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
|
||||
|
||||
class AccountsSettings(Document):
|
||||
@@ -22,35 +20,15 @@ class AccountsSettings(Document):
|
||||
|
||||
self.validate_stale_days()
|
||||
self.enable_payment_schedule_in_print()
|
||||
self.toggle_discount_accounting_fields()
|
||||
|
||||
def validate_stale_days(self):
|
||||
if not self.allow_stale and cint(self.stale_days) <= 0:
|
||||
frappe.msgprint(
|
||||
_("Stale Days should start from 1."), title='Error', indicator='red',
|
||||
"Stale Days should start from 1.", title='Error', indicator='red',
|
||||
raise_exception=1)
|
||||
|
||||
def enable_payment_schedule_in_print(self):
|
||||
show_in_print = cint(self.show_payment_schedule_in_print)
|
||||
for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
|
||||
make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False)
|
||||
make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check", validate_fields_for_doctype=False)
|
||||
|
||||
def toggle_discount_accounting_fields(self):
|
||||
enable_discount_accounting = cint(self.enable_discount_accounting)
|
||||
|
||||
for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
|
||||
make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
||||
if enable_discount_accounting:
|
||||
make_property_setter(doctype, "discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
|
||||
else:
|
||||
make_property_setter(doctype, "discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
|
||||
|
||||
for doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
make_property_setter(doctype, "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
||||
if enable_discount_accounting:
|
||||
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
|
||||
else:
|
||||
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
|
||||
|
||||
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
||||
make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check")
|
||||
make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check")
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-09-12 22:26:19.594367",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"add_deduct_tax",
|
||||
"charge_type",
|
||||
"row_id",
|
||||
"account_head",
|
||||
"col_break_1",
|
||||
"description",
|
||||
"included_in_paid_amount",
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
"section_break_8",
|
||||
"rate",
|
||||
"section_break_9",
|
||||
"currency",
|
||||
"tax_amount",
|
||||
"total",
|
||||
"allocated_amount",
|
||||
"column_break_13",
|
||||
"base_tax_amount",
|
||||
"base_total",
|
||||
"base_allocated_amount"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "charge_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"oldfieldname": "charge_type",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nActual\nOn Paid Amount\nOn Previous Row Amount\nOn Previous Row Total",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1",
|
||||
"fieldname": "row_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Reference Row #",
|
||||
"oldfieldname": "row_id",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "account_head",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Account Head",
|
||||
"oldfieldname": "account_head",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break_1",
|
||||
"fieldtype": "Column Break",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Small Text",
|
||||
"print_width": "300px",
|
||||
"reqd": 1,
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"fieldname": "accounting_dimensions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Accounting Dimensions"
|
||||
},
|
||||
{
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"oldfieldname": "cost_center_other_charges",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Cost Center"
|
||||
},
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"oldfieldname": "rate",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_9",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "tax_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "total",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Total",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_13",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "base_tax_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount (Company Currency)",
|
||||
"oldfieldname": "tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "base_total",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total (Company Currency)",
|
||||
"oldfieldname": "total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "add_deduct_tax",
|
||||
"fieldtype": "Select",
|
||||
"label": "Add Or Deduct",
|
||||
"options": "Add\nDeduct",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "included_in_paid_amount",
|
||||
"fieldtype": "Check",
|
||||
"label": "Considered In Paid Amount"
|
||||
},
|
||||
{
|
||||
"fieldname": "allocated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-09 11:46:58.373170",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Advance Taxes and Charges",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
}
|
||||
@@ -3,10 +3,8 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AllowedDimension(Document):
|
||||
pass
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AllowedToTransactWith(Document):
|
||||
pass
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class ApplicableOnAccount(Document):
|
||||
pass
|
||||
|
||||
@@ -42,9 +42,10 @@ let add_fields_to_mapping_table = function (frm) {
|
||||
});
|
||||
});
|
||||
|
||||
frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
|
||||
'bank_transaction_field', 'options', options
|
||||
);
|
||||
frappe.meta.get_docfield("Bank Transaction Mapping", "bank_transaction_field",
|
||||
frm.doc.name).options = options;
|
||||
|
||||
frm.fields_dict.bank_transaction_mapping.grid.refresh();
|
||||
};
|
||||
|
||||
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe.contacts.address_and_contact import (
|
||||
delete_contact_and_address,
|
||||
load_address_and_contact,
|
||||
)
|
||||
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):
|
||||
@@ -17,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)
|
||||
23
erpnext/accounts/doctype/bank/test_bank.js
Normal file
23
erpnext/accounts/doctype/bank/test_bank.js
Normal 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()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -3,8 +3,8 @@
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestBank(unittest.TestCase):
|
||||
pass
|
||||
|
||||
@@ -86,7 +86,6 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Setting the account as a Company Account is necessary for Bank Reconciliation",
|
||||
"fieldname": "is_company_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Company Account"
|
||||
@@ -208,7 +207,7 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-10-23 16:48:06.303658",
|
||||
"modified": "2020-07-17 13:59:50.795412",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank Account",
|
||||
|
||||
@@ -3,15 +3,10 @@
|
||||
# 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):
|
||||
|
||||
@@ -26,4 +26,4 @@ def get_data():
|
||||
'items': ['Journal Entry']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
23
erpnext/accounts/doctype/bank_account/test_bank_account.js
Normal file
23
erpnext/accounts/doctype/bank_account/test_bank_account.js
Normal 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()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -3,10 +3,10 @@
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import ValidationError
|
||||
from frappe import _
|
||||
from frappe import ValidationError
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Bank Account')
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class BankAccountSubtype(Document):
|
||||
pass
|
||||
|
||||
@@ -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()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -5,6 +5,5 @@ from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestBankAccountSubtype(unittest.TestCase):
|
||||
pass
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class BankAccountType(Document):
|
||||
pass
|
||||
|
||||
@@ -6,6 +6,5 @@ from __future__ import unicode_literals
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestBankAccountType(unittest.TestCase):
|
||||
pass
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt, 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"
|
||||
}
|
||||
|
||||
class BankClearance(Document):
|
||||
@frappe.whitelist()
|
||||
def get_payment_entries(self):
|
||||
if not (self.from_date and self.to_date):
|
||||
frappe.throw(_("From Date and To Date are Mandatory"))
|
||||
@@ -110,7 +108,6 @@ class BankClearance(Document):
|
||||
row.update(d)
|
||||
self.total_amount += flt(amount)
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_clearance_date(self):
|
||||
clearance_date_updated = False
|
||||
for d in self.get('payment_entries'):
|
||||
|
||||
@@ -6,6 +6,5 @@ from __future__ import unicode_literals
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestBankClearance(unittest.TestCase):
|
||||
pass
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
# 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
|
||||
@@ -3,14 +3,10 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
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):
|
||||
@@ -29,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]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user