mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 00:14:50 +00:00
Merge remote-tracking branch 'frappe/develop' into fix-reserve-qty
# Conflicts: # erpnext/stock/doctype/delivery_note/test_delivery_note.py
This commit is contained in:
2
.github/helper/.flake8_strict
vendored
2
.github/helper/.flake8_strict
vendored
@@ -66,6 +66,8 @@ ignore =
|
|||||||
F841,
|
F841,
|
||||||
E713,
|
E713,
|
||||||
E712,
|
E712,
|
||||||
|
B023,
|
||||||
|
B028
|
||||||
|
|
||||||
|
|
||||||
max-line-length = 200
|
max-line-length = 200
|
||||||
|
|||||||
99
.github/helper/documentation.py
vendored
99
.github/helper/documentation.py
vendored
@@ -3,52 +3,71 @@ import requests
|
|||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
docs_repos = [
|
WEBSITE_REPOS = [
|
||||||
"frappe_docs",
|
|
||||||
"erpnext_documentation",
|
|
||||||
"erpnext_com",
|
"erpnext_com",
|
||||||
"frappe_io",
|
"frappe_io",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
DOCUMENTATION_DOMAINS = [
|
||||||
|
"docs.erpnext.com",
|
||||||
|
"frappeframework.com",
|
||||||
|
]
|
||||||
|
|
||||||
def uri_validator(x):
|
|
||||||
result = urlparse(x)
|
|
||||||
return all([result.scheme, result.netloc, result.path])
|
|
||||||
|
|
||||||
def docs_link_exists(body):
|
def is_valid_url(url: str) -> bool:
|
||||||
for line in body.splitlines():
|
parts = urlparse(url)
|
||||||
for word in line.split():
|
return all((parts.scheme, parts.netloc, parts.path))
|
||||||
if word.startswith('http') and uri_validator(word):
|
|
||||||
parsed_url = urlparse(word)
|
|
||||||
if parsed_url.netloc == "github.com":
|
def is_documentation_link(word: str) -> bool:
|
||||||
parts = parsed_url.path.split('/')
|
if not word.startswith("http") or not is_valid_url(word):
|
||||||
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos:
|
return False
|
||||||
return True
|
|
||||||
elif parsed_url.netloc == "docs.erpnext.com":
|
parsed_url = urlparse(word)
|
||||||
return True
|
if parsed_url.netloc in DOCUMENTATION_DOMAINS:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if parsed_url.netloc == "github.com":
|
||||||
|
parts = parsed_url.path.split("/")
|
||||||
|
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in WEBSITE_REPOS:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def contains_documentation_link(body: str) -> bool:
|
||||||
|
return any(
|
||||||
|
is_documentation_link(word)
|
||||||
|
for line in body.splitlines()
|
||||||
|
for word in line.split()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_pull_request(number: str) -> "tuple[int, str]":
|
||||||
|
response = requests.get(f"https://api.github.com/repos/frappe/erpnext/pulls/{number}")
|
||||||
|
if not response.ok:
|
||||||
|
return 1, "Pull Request Not Found! ⚠️"
|
||||||
|
|
||||||
|
payload = response.json()
|
||||||
|
title = (payload.get("title") or "").lower().strip()
|
||||||
|
head_sha = (payload.get("head") or {}).get("sha")
|
||||||
|
body = (payload.get("body") or "").lower()
|
||||||
|
|
||||||
|
if (
|
||||||
|
not title.startswith("feat")
|
||||||
|
or not head_sha
|
||||||
|
or "no-docs" in body
|
||||||
|
or "backport" in body
|
||||||
|
):
|
||||||
|
return 0, "Skipping documentation checks... 🏃"
|
||||||
|
|
||||||
|
if contains_documentation_link(body):
|
||||||
|
return 0, "Documentation Link Found. You're Awesome! 🎉"
|
||||||
|
|
||||||
|
return 1, "Documentation Link Not Found! ⚠️"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pr = sys.argv[1]
|
exit_code, message = check_pull_request(sys.argv[1])
|
||||||
response = requests.get("https://api.github.com/repos/frappe/erpnext/pulls/{}".format(pr))
|
print(message)
|
||||||
|
sys.exit(exit_code)
|
||||||
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()
|
|
||||||
|
|
||||||
if (title.startswith("feat")
|
|
||||||
and head_sha
|
|
||||||
and "no-docs" not in body
|
|
||||||
and "backport" not in body
|
|
||||||
):
|
|
||||||
if docs_link_exists(body):
|
|
||||||
print("Documentation Link Found. You're Awesome! 🎉")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Documentation Link Not Found! ⚠️")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Skipping documentation checks... 🏃")
|
|
||||||
|
|||||||
36
.github/helper/install.sh
vendored
36
.github/helper/install.sh
vendored
@@ -2,13 +2,6 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Check for merge conflicts before proceeding
|
|
||||||
python -m compileall -f "${GITHUB_WORKSPACE}"
|
|
||||||
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
|
||||||
then echo "Found merge conflicts"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd ~ || exit
|
cd ~ || exit
|
||||||
|
|
||||||
sudo apt update && sudo apt install redis-server libcups2-dev
|
sudo apt update && sudo apt install redis-server libcups2-dev
|
||||||
@@ -31,15 +24,14 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
if [ "$DB" == "mariadb" ];then
|
if [ "$DB" == "mariadb" ];then
|
||||||
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 -proot -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 -proot -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 -proot -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 -proot -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 -proot -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 -proot -e "FLUSH PRIVILEGES"
|
||||||
mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DB" == "postgres" ];then
|
if [ "$DB" == "postgres" ];then
|
||||||
@@ -49,12 +41,17 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
install_whktml() {
|
install_whktml() {
|
||||||
wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
|
if [ "$(lsb_release -rs)" = "22.04" ]; then
|
||||||
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
wget -O /tmp/wkhtmltox.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
|
||||||
sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
sudo apt install /tmp/wkhtmltox.deb
|
||||||
sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
else
|
||||||
|
echo "Please update this script to support wkhtmltopdf for $(lsb_release -ds)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
install_whktml &
|
install_whktml &
|
||||||
|
wkpid=$!
|
||||||
|
|
||||||
|
|
||||||
cd ~/frappe-bench || exit
|
cd ~/frappe-bench || exit
|
||||||
|
|
||||||
@@ -63,10 +60,13 @@ sed -i 's/schedule:/# schedule:/g' Procfile
|
|||||||
sed -i 's/socketio:/# socketio:/g' Procfile
|
sed -i 's/socketio:/# socketio:/g' Procfile
|
||||||
sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
|
sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
|
||||||
|
|
||||||
|
bench get-app payments
|
||||||
bench get-app erpnext "${GITHUB_WORKSPACE}"
|
bench get-app erpnext "${GITHUB_WORKSPACE}"
|
||||||
|
|
||||||
if [ "$TYPE" == "server" ]; then bench setup requirements --dev; fi
|
if [ "$TYPE" == "server" ]; then bench setup requirements --dev; fi
|
||||||
|
|
||||||
|
wait $wkpid
|
||||||
|
|
||||||
bench start &> bench_run_logs.txt &
|
bench start &> bench_run_logs.txt &
|
||||||
CI=Yes bench build --app frappe &
|
CI=Yes bench build --app frappe &
|
||||||
bench --site test_site reinstall --yes
|
bench --site test_site reinstall --yes
|
||||||
|
|||||||
4
.github/helper/site_config_mariadb.json
vendored
4
.github/helper/site_config_mariadb.json
vendored
@@ -9,8 +9,8 @@
|
|||||||
"mail_password": "test",
|
"mail_password": "test",
|
||||||
"admin_password": "admin",
|
"admin_password": "admin",
|
||||||
"root_login": "root",
|
"root_login": "root",
|
||||||
"root_password": "travis",
|
"root_password": "root",
|
||||||
"host_name": "http://test_site:8000",
|
"host_name": "http://test_site:8000",
|
||||||
"install_apps": ["erpnext"],
|
"install_apps": ["payments", "erpnext"],
|
||||||
"throttle_user_limit": 100
|
"throttle_user_limit": 100
|
||||||
}
|
}
|
||||||
|
|||||||
2
.github/workflows/docs-checker.yml
vendored
2
.github/workflows/docs-checker.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
- name: 'Setup Environment'
|
- name: 'Setup Environment'
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: '3.10'
|
||||||
|
|
||||||
- name: 'Clone repo'
|
- name: 'Clone repo'
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|||||||
32
.github/workflows/initiate_release.yml
vendored
Normal file
32
.github/workflows/initiate_release.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# This workflow is agnostic to branches. Only maintain on develop branch.
|
||||||
|
# To add/remove versions just modify the matrix.
|
||||||
|
|
||||||
|
name: Create weekly release pull requests
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# 9:30 UTC => 3 PM IST Tuesday
|
||||||
|
- cron: "30 9 * * 2"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
version: ["13", "14"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: octokit/request-action@v2.x
|
||||||
|
with:
|
||||||
|
route: POST /repos/{owner}/{repo}/pulls
|
||||||
|
owner: frappe
|
||||||
|
repo: erpnext
|
||||||
|
title: |-
|
||||||
|
"chore: release v${{ matrix.version }}"
|
||||||
|
body: "Automated weekly release."
|
||||||
|
base: version-${{ matrix.version }}
|
||||||
|
head: version-${{ matrix.version }}-hotfix
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
16
.github/workflows/linters.yml
vendored
16
.github/workflows/linters.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Python 3.8
|
- name: Set up Python 3.10
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: '3.10'
|
||||||
|
|
||||||
- name: Install and Run Pre-commit
|
- name: Install and Run Pre-commit
|
||||||
uses: pre-commit/action@v2.0.3
|
uses: pre-commit/action@v2.0.3
|
||||||
@@ -22,10 +22,8 @@ jobs:
|
|||||||
- name: Download Semgrep rules
|
- name: Download Semgrep rules
|
||||||
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
|
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
|
||||||
|
|
||||||
- uses: returntocorp/semgrep-action@v1
|
- name: Download semgrep
|
||||||
env:
|
run: pip install semgrep==0.97.0
|
||||||
SEMGREP_TIMEOUT: 120
|
|
||||||
with:
|
- name: Run Semgrep rules
|
||||||
config: >-
|
run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness
|
||||||
r/python.lang.correctness
|
|
||||||
./frappe-semgrep-rules/rules
|
|
||||||
|
|||||||
39
.github/workflows/patch.yml
vendored
39
.github/workflows/patch.yml
vendored
@@ -11,7 +11,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: patch-develop-${{ github.event.number }}
|
group: patch-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
mysql:
|
mysql:
|
||||||
image: mariadb:10.3
|
image: mariadb:10.3
|
||||||
env:
|
env:
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: YES
|
MARIADB_ROOT_PASSWORD: 'root'
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||||
@@ -34,10 +34,18 @@ jobs:
|
|||||||
- name: Clone
|
- name: Clone
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Check for valid Python & Merge Conflicts
|
||||||
|
run: |
|
||||||
|
python -m compileall -f "${GITHUB_WORKSPACE}"
|
||||||
|
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||||
|
then echo "Found merge conflicts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: "gabrielfalcao/pyenv-action@v9"
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
versions: 3.10:latest, 3.7:latest
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
@@ -52,7 +60,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
@@ -82,7 +90,10 @@ jobs:
|
|||||||
${{ runner.os }}-yarn-
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
run: |
|
||||||
|
pip install frappe-bench
|
||||||
|
pyenv global $(pyenv versions | grep '3.10')
|
||||||
|
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||||
env:
|
env:
|
||||||
DB: mariadb
|
DB: mariadb
|
||||||
TYPE: server
|
TYPE: server
|
||||||
@@ -96,6 +107,7 @@ jobs:
|
|||||||
git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
|
git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
|
||||||
git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
|
git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
|
||||||
|
|
||||||
|
pyenv global $(pyenv versions | grep '3.7')
|
||||||
for version in $(seq 12 13)
|
for version in $(seq 12 13)
|
||||||
do
|
do
|
||||||
echo "Updating to v$version"
|
echo "Updating to v$version"
|
||||||
@@ -107,7 +119,11 @@ jobs:
|
|||||||
git -C "apps/frappe" checkout -q -f $branch_name
|
git -C "apps/frappe" checkout -q -f $branch_name
|
||||||
git -C "apps/erpnext" checkout -q -f $branch_name
|
git -C "apps/erpnext" checkout -q -f $branch_name
|
||||||
|
|
||||||
bench setup requirements --python
|
rm -rf ~/frappe-bench/env
|
||||||
|
bench setup env
|
||||||
|
bench pip install -e ./apps/payments
|
||||||
|
bench pip install -e ./apps/erpnext
|
||||||
|
|
||||||
bench --site test_site migrate
|
bench --site test_site migrate
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -115,5 +131,12 @@ jobs:
|
|||||||
echo "Updating to latest version"
|
echo "Updating to latest version"
|
||||||
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
|
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
|
||||||
git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
|
git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
|
||||||
bench setup requirements --python
|
|
||||||
|
pyenv global $(pyenv versions | grep '3.10')
|
||||||
|
rm -rf ~/frappe-bench/env
|
||||||
|
bench -v setup env
|
||||||
|
bench pip install -e ./apps/payments
|
||||||
|
bench pip install -e ./apps/erpnext
|
||||||
|
|
||||||
bench --site test_site migrate
|
bench --site test_site migrate
|
||||||
|
bench --site test_site install-app payments
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup Node.js v14
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 14
|
node-version: 18
|
||||||
- name: Setup dependencies
|
- name: Setup dependencies
|
||||||
run: |
|
run: |
|
||||||
npm install @semantic-release/git @semantic-release/exec --no-save
|
npm install @semantic-release/git @semantic-release/exec --no-save
|
||||||
@@ -28,4 +28,4 @@ jobs:
|
|||||||
GIT_AUTHOR_EMAIL: "developers@frappe.io"
|
GIT_AUTHOR_EMAIL: "developers@frappe.io"
|
||||||
GIT_COMMITTER_NAME: "Frappe PR Bot"
|
GIT_COMMITTER_NAME: "Frappe PR Bot"
|
||||||
GIT_COMMITTER_EMAIL: "developers@frappe.io"
|
GIT_COMMITTER_EMAIL: "developers@frappe.io"
|
||||||
run: npx semantic-release
|
run: npx semantic-release
|
||||||
|
|||||||
30
.github/workflows/semantic-commits.yml
vendored
Normal file
30
.github/workflows/semantic-commits.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: Semantic Commits
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: commitcheck-erpnext-${{ github.event.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
commitlint:
|
||||||
|
name: Check Commit Titles
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 200
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
|
- name: Check commit titles
|
||||||
|
run: |
|
||||||
|
npm install @commitlint/cli @commitlint/config-conventional
|
||||||
|
npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}
|
||||||
26
.github/workflows/server-tests-mariadb.yml
vendored
26
.github/workflows/server-tests-mariadb.yml
vendored
@@ -16,18 +16,18 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
user:
|
user:
|
||||||
description: 'user'
|
description: 'Frappe Framework repository user (add your username for forks)'
|
||||||
required: true
|
required: true
|
||||||
default: 'frappe'
|
default: 'frappe'
|
||||||
type: string
|
type: string
|
||||||
branch:
|
branch:
|
||||||
description: 'Branch name'
|
description: 'Frappe Framework branch'
|
||||||
default: 'develop'
|
default: 'develop'
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: server-mariadb-develop-${{ github.event.number }}
|
group: server-mariadb-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -39,15 +39,15 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
container: [1, 2, 3]
|
container: [1, 2, 3, 4]
|
||||||
|
|
||||||
name: Python Unit Tests
|
name: Python Unit Tests
|
||||||
|
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
image: mariadb:10.3
|
image: mariadb:10.6
|
||||||
env:
|
env:
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: YES
|
MARIADB_ROOT_PASSWORD: 'root'
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||||
@@ -59,7 +59,15 @@ jobs:
|
|||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Check for valid Python & Merge Conflicts
|
||||||
|
run: |
|
||||||
|
python -m compileall -f "${GITHUB_WORKSPACE}"
|
||||||
|
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||||
|
then echo "Found merge conflicts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
@@ -74,7 +82,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
@@ -112,7 +120,7 @@ jobs:
|
|||||||
FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
|
FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage
|
run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --with-coverage --total-builds 4 --build-number ${{ matrix.container }}'
|
||||||
env:
|
env:
|
||||||
TYPE: server
|
TYPE: server
|
||||||
CI_BUILD_ID: ${{ github.run_id }}
|
CI_BUILD_ID: ${{ github.run_id }}
|
||||||
|
|||||||
17
.github/workflows/server-tests-postgres.yml
vendored
17
.github/workflows/server-tests-postgres.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
types: [opened, labelled, synchronize, reopened]
|
types: [opened, labelled, synchronize, reopened]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: server-postgres-develop-${{ github.event.number }}
|
group: server-postgres-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
container: [1, 2, 3]
|
container: [1]
|
||||||
|
|
||||||
name: Python Unit Tests
|
name: Python Unit Tests
|
||||||
|
|
||||||
@@ -46,7 +46,15 @@ jobs:
|
|||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: '3.10'
|
||||||
|
|
||||||
|
- name: Check for valid Python & Merge Conflicts
|
||||||
|
run: |
|
||||||
|
python -m compileall -f "${GITHUB_WORKSPACE}"
|
||||||
|
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||||
|
then echo "Found merge conflicts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
@@ -61,7 +69,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
@@ -90,7 +98,6 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-yarn-
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pull_request_rules:
|
|||||||
- author!=nabinhait
|
- author!=nabinhait
|
||||||
- author!=ankush
|
- author!=ankush
|
||||||
- author!=deepeshgarg007
|
- author!=deepeshgarg007
|
||||||
|
- author!=frappe-pr-bot
|
||||||
- author!=mergify[bot]
|
- author!=mergify[bot]
|
||||||
|
|
||||||
- or:
|
- or:
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ repos:
|
|||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: check-ast
|
- id: check-ast
|
||||||
|
|
||||||
- repo: https://gitlab.com/pycqa/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 3.9.2
|
rev: 5.0.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies: [
|
additional_dependencies: [
|
||||||
@@ -32,8 +32,8 @@ repos:
|
|||||||
- id: black
|
- id: black
|
||||||
additional_dependencies: ['click==8.0.4']
|
additional_dependencies: ['click==8.0.4']
|
||||||
|
|
||||||
- repo: https://github.com/timothycrosley/isort
|
- repo: https://github.com/PyCQA/isort
|
||||||
rev: 5.9.1
|
rev: 5.12.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: isort
|
||||||
exclude: ".*setup.py$"
|
exclude: ".*setup.py$"
|
||||||
|
|||||||
37
CODEOWNERS
37
CODEOWNERS
@@ -3,33 +3,26 @@
|
|||||||
# These owners will be the default owners for everything in
|
# These owners will be the default owners for everything in
|
||||||
# the repo. Unless a later match takes precedence,
|
# the repo. Unless a later match takes precedence,
|
||||||
|
|
||||||
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007
|
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/assets/ @nextchamp-saqib @deepeshgarg007
|
erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/erpnext_integrations/ @nextchamp-saqib
|
|
||||||
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
|
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
|
||||||
erpnext/regional @nextchamp-saqib @deepeshgarg007
|
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/selling @nextchamp-saqib @deepeshgarg007
|
erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/support/ @nextchamp-saqib @deepeshgarg007
|
erpnext/support/ @nextchamp-saqib @deepeshgarg007
|
||||||
pos* @nextchamp-saqib
|
pos* @nextchamp-saqib
|
||||||
|
|
||||||
erpnext/buying/ @marination @rohitwaghchaure @ankush
|
erpnext/buying/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/e_commerce/ @marination
|
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/maintenance/ @marination @rohitwaghchaure
|
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/manufacturing/ @marination @rohitwaghchaure @ankush
|
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/portal/ @marination
|
erpnext/stock/ @rohitwaghchaure @s-aga-r
|
||||||
erpnext/quality_management/ @marination @rohitwaghchaure
|
|
||||||
erpnext/shopping_cart/ @marination
|
|
||||||
erpnext/stock/ @marination @rohitwaghchaure @ankush
|
|
||||||
|
|
||||||
erpnext/crm/ @ruchamahabal @pateljannat
|
erpnext/crm/ @NagariaHussain
|
||||||
erpnext/education/ @ruchamahabal @pateljannat
|
erpnext/education/ @rutwikhdev
|
||||||
erpnext/hr/ @ruchamahabal @pateljannat
|
erpnext/projects/ @ruchamahabal
|
||||||
erpnext/payroll @ruchamahabal @pateljannat
|
|
||||||
erpnext/projects/ @ruchamahabal @pateljannat
|
|
||||||
|
|
||||||
erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination @ankush
|
erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure
|
||||||
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @marination @ankush
|
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib
|
||||||
erpnext/public/ @nextchamp-saqib @marination
|
|
||||||
|
|
||||||
.github/ @ankush
|
.github/ @ankush
|
||||||
requirements.txt @gavindsouza
|
pyproject.toml @ankush
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ GNU/General Public License (see [license.txt](license.txt))
|
|||||||
|
|
||||||
The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
|
The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
|
||||||
|
|
||||||
|
By contributing to ERPNext, you agree that your contributions will be licensed under its GNU General Public License (v3).
|
||||||
|
|
||||||
## Logo and Trademark Policy
|
## Logo and Trademark Policy
|
||||||
|
|
||||||
Please read our [Logo and Trademark Policy](TRADEMARK_POLICY.md).
|
Please read our [Logo and Trademark Policy](TRADEMARK_POLICY.md).
|
||||||
|
|||||||
25
commitlint.config.js
Normal file
25
commitlint.config.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
module.exports = {
|
||||||
|
parserPreset: 'conventional-changelog-conventionalcommits',
|
||||||
|
rules: {
|
||||||
|
'subject-empty': [2, 'never'],
|
||||||
|
'type-case': [2, 'always', 'lower-case'],
|
||||||
|
'type-empty': [2, 'never'],
|
||||||
|
'type-enum': [
|
||||||
|
2,
|
||||||
|
'always',
|
||||||
|
[
|
||||||
|
'build',
|
||||||
|
'chore',
|
||||||
|
'ci',
|
||||||
|
'docs',
|
||||||
|
'feat',
|
||||||
|
'fix',
|
||||||
|
'perf',
|
||||||
|
'refactor',
|
||||||
|
'revert',
|
||||||
|
'style',
|
||||||
|
'test',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
hypothesis~=6.31.0
|
|
||||||
@@ -76,7 +76,7 @@ def get(
|
|||||||
|
|
||||||
def build_result(account, dates, gl_entries):
|
def build_result(account, dates, gl_entries):
|
||||||
result = [[getdate(date), 0.0] for date in dates]
|
result = [[getdate(date), 0.0] for date in dates]
|
||||||
root_type = frappe.db.get_value("Account", account, "root_type")
|
root_type = frappe.get_cached_value("Account", account, "root_type")
|
||||||
|
|
||||||
# start with the first date
|
# start with the first date
|
||||||
date_index = 0
|
date_index = 0
|
||||||
|
|||||||
@@ -378,7 +378,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# check if books nor frozen till endate:
|
# check if books nor frozen till endate:
|
||||||
if accounts_frozen_upto and (end_date) <= getdate(accounts_frozen_upto):
|
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
|
||||||
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
|
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
|
||||||
|
|
||||||
if via_journal_entry:
|
if via_journal_entry:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class Account(NestedSet):
|
|||||||
def autoname(self):
|
def autoname(self):
|
||||||
from erpnext.accounts.utils import get_autoname_with_number
|
from erpnext.accounts.utils import get_autoname_with_number
|
||||||
|
|
||||||
self.name = get_autoname_with_number(self.account_number, self.account_name, None, self.company)
|
self.name = get_autoname_with_number(self.account_number, self.account_name, self.company)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
from erpnext.accounts.utils import validate_field_number
|
from erpnext.accounts.utils import validate_field_number
|
||||||
@@ -58,7 +58,7 @@ class Account(NestedSet):
|
|||||||
def validate_parent(self):
|
def validate_parent(self):
|
||||||
"""Fetch Parent Details and validate parent account"""
|
"""Fetch Parent Details and validate parent account"""
|
||||||
if self.parent_account:
|
if self.parent_account:
|
||||||
par = frappe.db.get_value(
|
par = frappe.get_cached_value(
|
||||||
"Account", self.parent_account, ["name", "is_group", "company"], as_dict=1
|
"Account", self.parent_account, ["name", "is_group", "company"], as_dict=1
|
||||||
)
|
)
|
||||||
if not par:
|
if not par:
|
||||||
@@ -82,7 +82,7 @@ class Account(NestedSet):
|
|||||||
|
|
||||||
def set_root_and_report_type(self):
|
def set_root_and_report_type(self):
|
||||||
if self.parent_account:
|
if self.parent_account:
|
||||||
par = frappe.db.get_value(
|
par = frappe.get_cached_value(
|
||||||
"Account", self.parent_account, ["report_type", "root_type"], as_dict=1
|
"Account", self.parent_account, ["report_type", "root_type"], as_dict=1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ class Account(NestedSet):
|
|||||||
self.root_type = par.root_type
|
self.root_type = par.root_type
|
||||||
|
|
||||||
if self.is_group:
|
if self.is_group:
|
||||||
db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
|
db_value = self.get_doc_before_save()
|
||||||
if db_value:
|
if db_value:
|
||||||
if self.report_type != db_value.report_type:
|
if self.report_type != db_value.report_type:
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
@@ -111,13 +111,13 @@ class Account(NestedSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_root_details(self):
|
def validate_root_details(self):
|
||||||
# does not exists parent
|
doc_before_save = self.get_doc_before_save()
|
||||||
if frappe.db.exists("Account", self.name):
|
|
||||||
if not frappe.db.get_value("Account", self.name, "parent_account"):
|
if doc_before_save and not doc_before_save.parent_account:
|
||||||
throw(_("Root cannot be edited."), RootNotEditable)
|
throw(_("Root cannot be edited."), RootNotEditable)
|
||||||
|
|
||||||
if not self.parent_account and not self.is_group:
|
if not self.parent_account and not self.is_group:
|
||||||
frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name)))
|
throw(_("The root account {0} must be a group").format(frappe.bold(self.name)))
|
||||||
|
|
||||||
def validate_root_company_and_sync_account_to_children(self):
|
def validate_root_company_and_sync_account_to_children(self):
|
||||||
# ignore validation while creating new compnay or while syncing to child companies
|
# ignore validation while creating new compnay or while syncing to child companies
|
||||||
@@ -127,7 +127,9 @@ class Account(NestedSet):
|
|||||||
return
|
return
|
||||||
ancestors = get_root_company(self.company)
|
ancestors = get_root_company(self.company)
|
||||||
if ancestors:
|
if ancestors:
|
||||||
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
|
if frappe.get_cached_value(
|
||||||
|
"Company", self.company, "allow_account_creation_against_child_company"
|
||||||
|
):
|
||||||
return
|
return
|
||||||
if not frappe.db.get_value(
|
if not frappe.db.get_value(
|
||||||
"Account", {"account_name": self.account_name, "company": ancestors[0]}, "name"
|
"Account", {"account_name": self.account_name, "company": ancestors[0]}, "name"
|
||||||
@@ -138,7 +140,7 @@ class Account(NestedSet):
|
|||||||
if not descendants:
|
if not descendants:
|
||||||
return
|
return
|
||||||
parent_acc_name_map = {}
|
parent_acc_name_map = {}
|
||||||
parent_acc_name, parent_acc_number = frappe.db.get_value(
|
parent_acc_name, parent_acc_number = frappe.get_cached_value(
|
||||||
"Account", self.parent_account, ["account_name", "account_number"]
|
"Account", self.parent_account, ["account_name", "account_number"]
|
||||||
)
|
)
|
||||||
filters = {
|
filters = {
|
||||||
@@ -159,27 +161,28 @@ class Account(NestedSet):
|
|||||||
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
||||||
|
|
||||||
def validate_group_or_ledger(self):
|
def validate_group_or_ledger(self):
|
||||||
if self.get("__islocal"):
|
doc_before_save = self.get_doc_before_save()
|
||||||
|
if not doc_before_save or cint(doc_before_save.is_group) == cint(self.is_group):
|
||||||
return
|
return
|
||||||
|
|
||||||
existing_is_group = frappe.db.get_value("Account", self.name, "is_group")
|
if self.check_gle_exists():
|
||||||
if cint(self.is_group) != cint(existing_is_group):
|
throw(_("Account with existing transaction cannot be converted to ledger"))
|
||||||
if self.check_gle_exists():
|
elif self.is_group:
|
||||||
throw(_("Account with existing transaction cannot be converted to ledger"))
|
if self.account_type and not self.flags.exclude_account_type_check:
|
||||||
elif self.is_group:
|
throw(_("Cannot covert to Group because Account Type is selected."))
|
||||||
if self.account_type and not self.flags.exclude_account_type_check:
|
elif self.check_if_child_exists():
|
||||||
throw(_("Cannot covert to Group because Account Type is selected."))
|
throw(_("Account with child nodes cannot be set as ledger"))
|
||||||
elif self.check_if_child_exists():
|
|
||||||
throw(_("Account with child nodes cannot be set as ledger"))
|
|
||||||
|
|
||||||
def validate_frozen_accounts_modifier(self):
|
def validate_frozen_accounts_modifier(self):
|
||||||
old_value = frappe.db.get_value("Account", self.name, "freeze_account")
|
doc_before_save = self.get_doc_before_save()
|
||||||
if old_value and old_value != self.freeze_account:
|
if not doc_before_save or doc_before_save.freeze_account == self.freeze_account:
|
||||||
frozen_accounts_modifier = frappe.db.get_value(
|
return
|
||||||
"Accounts Settings", None, "frozen_accounts_modifier"
|
|
||||||
)
|
frozen_accounts_modifier = frappe.get_cached_value(
|
||||||
if not frozen_accounts_modifier or frozen_accounts_modifier not in frappe.get_roles():
|
"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
|
||||||
throw(_("You are not authorized to set Frozen value"))
|
)
|
||||||
|
if not frozen_accounts_modifier or frozen_accounts_modifier not in frappe.get_roles():
|
||||||
|
throw(_("You are not authorized to set Frozen value"))
|
||||||
|
|
||||||
def validate_balance_must_be_debit_or_credit(self):
|
def validate_balance_must_be_debit_or_credit(self):
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
@@ -223,9 +226,9 @@ class Account(NestedSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# validate if parent of child company account to be added is a group
|
# validate if parent of child company account to be added is a group
|
||||||
if frappe.db.get_value("Account", self.parent_account, "is_group") and not frappe.db.get_value(
|
if frappe.get_cached_value(
|
||||||
"Account", parent_acc_name_map[company], "is_group"
|
"Account", self.parent_account, "is_group"
|
||||||
):
|
) and not frappe.get_cached_value("Account", parent_acc_name_map[company], "is_group"):
|
||||||
msg = _(
|
msg = _(
|
||||||
"While creating account for Child Company {0}, parent account {1} found as a ledger account."
|
"While creating account for Child Company {0}, parent account {1} found as a ledger account."
|
||||||
).format(company_bold, parent_acc_name_bold)
|
).format(company_bold, parent_acc_name_bold)
|
||||||
@@ -377,17 +380,15 @@ def validate_account_number(name, account_number, company):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_account_number(name, account_name, account_number=None, from_descendant=False):
|
def update_account_number(name, account_name, account_number=None, from_descendant=False):
|
||||||
account = frappe.db.get_value("Account", name, "company", as_dict=True)
|
account = frappe.get_cached_doc("Account", name)
|
||||||
if not account:
|
if not account:
|
||||||
return
|
return
|
||||||
|
|
||||||
old_acc_name, old_acc_number = frappe.db.get_value(
|
old_acc_name, old_acc_number = account.account_name, account.account_number
|
||||||
"Account", name, ["account_name", "account_number"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# check if account exists in parent company
|
# check if account exists in parent company
|
||||||
ancestors = get_ancestors_of("Company", account.company)
|
ancestors = get_ancestors_of("Company", account.company)
|
||||||
allow_independent_account_creation = frappe.get_value(
|
allow_independent_account_creation = frappe.get_cached_value(
|
||||||
"Company", account.company, "allow_account_creation_against_child_company"
|
"Company", account.company, "allow_account_creation_against_child_company"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -435,22 +436,24 @@ def update_account_number(name, account_name, account_number=None, from_descenda
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def merge_account(old, new, is_group, root_type, company):
|
def merge_account(old, new, is_group, root_type, company):
|
||||||
# Validate properties before merging
|
# Validate properties before merging
|
||||||
if not frappe.db.exists("Account", new):
|
new_account = frappe.get_cached_doc("Account", new)
|
||||||
|
|
||||||
|
if not new_account:
|
||||||
throw(_("Account {0} does not exist").format(new))
|
throw(_("Account {0} does not exist").format(new))
|
||||||
|
|
||||||
val = list(frappe.db.get_value("Account", new, ["is_group", "root_type", "company"]))
|
if (new_account.is_group, new_account.root_type, new_account.company) != (
|
||||||
|
cint(is_group),
|
||||||
if val != [cint(is_group), root_type, company]:
|
root_type,
|
||||||
|
company,
|
||||||
|
):
|
||||||
throw(
|
throw(
|
||||||
_(
|
_(
|
||||||
"""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""
|
"""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_group and frappe.db.get_value("Account", new, "parent_account") == old:
|
if is_group and new_account.parent_account == old:
|
||||||
frappe.db.set_value(
|
new_account.db_set("parent_account", frappe.get_cached_value("Account", old, "parent_account"))
|
||||||
"Account", new, "parent_account", frappe.db.get_value("Account", old, "parent_account")
|
|
||||||
)
|
|
||||||
|
|
||||||
frappe.rename_doc("Account", old, new, merge=1, force=1)
|
frappe.rename_doc("Account", old, new, merge=1, force=1)
|
||||||
|
|
||||||
|
|||||||
@@ -56,36 +56,41 @@ frappe.treeview_settings["Account"] = {
|
|||||||
accounts = nodes;
|
accounts = nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const get_balances = frappe.call({
|
frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => {
|
||||||
method: 'erpnext.accounts.utils.get_account_balances',
|
if(value) {
|
||||||
args: {
|
|
||||||
accounts: accounts,
|
|
||||||
company: cur_tree.args.company
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
get_balances.then(r => {
|
const get_balances = frappe.call({
|
||||||
if (!r.message || r.message.length == 0) return;
|
method: 'erpnext.accounts.utils.get_account_balances',
|
||||||
|
args: {
|
||||||
|
accounts: accounts,
|
||||||
|
company: cur_tree.args.company
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
for (let account of r.message) {
|
get_balances.then(r => {
|
||||||
|
if (!r.message || r.message.length == 0) return;
|
||||||
|
|
||||||
const node = cur_tree.nodes && cur_tree.nodes[account.value];
|
for (let account of r.message) {
|
||||||
if (!node || node.is_root) continue;
|
|
||||||
|
|
||||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
const node = cur_tree.nodes && cur_tree.nodes[account.value];
|
||||||
const balance = account.balance_in_account_currency || account.balance;
|
if (!node || node.is_root) continue;
|
||||||
const dr_or_cr = balance > 0 ? "Dr": "Cr";
|
|
||||||
const format = (value, currency) => format_currency(Math.abs(value), currency);
|
|
||||||
|
|
||||||
if (account.balance!==undefined) {
|
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||||
node.parent && node.parent.find('.balance-area').remove();
|
const balance = account.balance_in_account_currency || account.balance;
|
||||||
$('<span class="balance-area pull-right">'
|
const dr_or_cr = balance > 0 ? "Dr": "Cr";
|
||||||
+ (account.balance_in_account_currency ?
|
const format = (value, currency) => format_currency(Math.abs(value), currency);
|
||||||
(format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
|
|
||||||
+ format(account.balance, account.company_currency)
|
if (account.balance!==undefined) {
|
||||||
+ " " + dr_or_cr
|
node.parent && node.parent.find('.balance-area').remove();
|
||||||
+ '</span>').insertBefore(node.$ul);
|
$('<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ def create_charts(
|
|||||||
"account_number": account_number,
|
"account_number": account_number,
|
||||||
"account_type": child.get("account_type"),
|
"account_type": child.get("account_type"),
|
||||||
"account_currency": child.get("account_currency")
|
"account_currency": child.get("account_currency")
|
||||||
or frappe.db.get_value("Company", company, "default_currency"),
|
or frappe.get_cached_value("Company", company, "default_currency"),
|
||||||
"tax_rate": child.get("tax_rate"),
|
"tax_rate": child.get("tax_rate"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -148,7 +148,7 @@ def get_charts_for_country(country, with_standard=False):
|
|||||||
) or frappe.local.flags.allow_unverified_charts:
|
) or frappe.local.flags.allow_unverified_charts:
|
||||||
charts.append(content["name"])
|
charts.append(content["name"])
|
||||||
|
|
||||||
country_code = frappe.db.get_value("Country", country, "code")
|
country_code = frappe.get_cached_value("Country", country, "code")
|
||||||
if country_code:
|
if country_code:
|
||||||
folders = ("verified",)
|
folders = ("verified",)
|
||||||
if frappe.local.flags.allow_unverified_charts:
|
if frappe.local.flags.allow_unverified_charts:
|
||||||
|
|||||||
@@ -2,397 +2,438 @@
|
|||||||
"country_code": "at",
|
"country_code": "at",
|
||||||
"name": "Austria - Chart of Accounts",
|
"name": "Austria - Chart of Accounts",
|
||||||
"tree": {
|
"tree": {
|
||||||
"Summe Abschreibungen und Aufwendungen": {
|
"Klasse 0 Aktiva: Anlageverm\u00f6gen": {
|
||||||
"7010 bis 7080 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {},
|
"0100 Konzessionen ": {"account_type": "Fixed Asset"},
|
||||||
"7100 bis 7190 Sonstige Steuern": {
|
"0110 Patentrechte und Lizenzen ": {"account_type": "Fixed Asset"},
|
||||||
"account_type": "Tax"
|
"0120 Datenverarbeitungsprogramme ": {"account_type": "Fixed Asset"},
|
||||||
},
|
"0130 Marken, Warenzeichen und Musterschutzrechte, sonstige Urheberrechte ": {"account_type": "Fixed Asset"},
|
||||||
"7200 bis 7290 Instandhaltung u. Reinigung durh Dritte, Entsorgung, Beleuchtung": {},
|
"0140 Pacht- und Mietrechte ": {"account_type": "Fixed Asset"},
|
||||||
"7300 bis 7310 Transporte durch Dritte": {},
|
"0150 Bezugs- und ähnliche Rechte ": {"account_type": "Fixed Asset"},
|
||||||
"7320 bis 7330 Kfz - Aufwand": {},
|
"0160 Geschäfts-/Firmenwert ": {"account_type": "Fixed Asset"},
|
||||||
"7340 bis 7350 Reise- und Fahraufwand": {},
|
"0170 Umgründungsmehrwert ": {"account_type": "Fixed Asset"},
|
||||||
"7360 bis 7370 Tag- und N\u00e4chtigungsgelder": {},
|
"0180 Geleistete Anzahlungen auf immaterielle Vermögensgegenstände": {"account_type": "Fixed Asset"},
|
||||||
"7380 bis 7390 Nachrichtenaufwand": {},
|
"0190 Kumulierte Abschreibungen zu immateriellen Vermögensgegenständen ": {"account_type": "Fixed Asset"},
|
||||||
"7400 bis 7430 Miet- und Pachtaufwand": {},
|
"0200 Unbebaute Grundstücke, soweit nicht landwirtschaftlich genutzt ": {"account_type": "Fixed Asset"},
|
||||||
"7440 bis 7470 Leasingaufwand": {},
|
"0210 Bebaute Grundstücke (Grundwert) ": {"account_type": "Fixed Asset"},
|
||||||
"7480 bis 7490 Lizenzaufwand": {},
|
"0220 Landwirtschaftlich genutzte Grundstücke ": {"account_type": "Fixed Asset"},
|
||||||
"7500 bis 7530 Aufwand f\u00fcr beigestelltes Personal": {},
|
"0230 Grundstücksgleiche Rechte ": {"account_type": "Fixed Asset"},
|
||||||
"7540 bis 7570 Provisionen an Dritte": {},
|
"0300 Betriebs- und Geschäftsgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
|
||||||
"7580 bis 7590 Aufsichtsratsverg\u00fctungen": {},
|
"0310 Wohn- und Sozialgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
|
||||||
"7610 bis 7620 Druckerzeugnisse und Vervielf\u00e4ltigungen": {},
|
"0320 Betriebs- und Geschäftsgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
|
||||||
"7650 bis 7680 Werbung und Repr\u00e4sentationen": {},
|
"0330 Wohn- und Sozialgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
|
||||||
"7700 bis 7740 Versicherungen": {},
|
"0340 Grundstückseinrichtungen auf eigenem Grund ": {"account_type": "Fixed Asset"},
|
||||||
"7750 bis 7760 Beratungs- und Pr\u00fcfungsaufwand": {},
|
"0350 Grundstückseinrichtungen auf fremdem Grund ": {"account_type": "Fixed Asset"},
|
||||||
"7800 bis 7810 Schadensf\u00e4lle": {},
|
"0360 Bauliche Investitionen in fremden (gepachteten) Betriebs- und Geschäftsgebäuden": {"account_type": "Fixed Asset"},
|
||||||
"7840 bis 7880 Verschiedene betriebliche Aufwendungen": {},
|
"0370 Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgebäuden": {"account_type": "Fixed Asset"},
|
||||||
"7910 bis 7950 Aufwandsstellenrechung der Hersteller": {},
|
"0390 Kumulierte Abschreibungen zu Grundstücken ": {"account_type": "Fixed Asset"},
|
||||||
"Abschreibungen auf aktivierte Aufwendungen f\u00fcr das Ingangs. u. Erweitern des Betriebes": {},
|
"0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"},
|
||||||
"Abschreibungen vom Umlaufverm\u00f6gen, soweit diese die im Unternehmen \u00fcblichen Abschreibungen \u00fcbersteigen": {},
|
"0500 Maschinenwerkzeuge ": {"account_type": "Fixed Asset"},
|
||||||
"Aufwandsstellenrechnung": {},
|
"0510 Allgemeine Werkzeuge und Handwerkzeuge ": {"account_type": "Fixed Asset"},
|
||||||
"Aus- und Fortbildung": {},
|
"0520 Prototypen, Formen, Modelle ": {"account_type": "Fixed Asset"},
|
||||||
"Buchwert abgegangener Anlagen, ausgenommen Finanzanlagen": {},
|
"0530 Andere Erzeugungshilfsmittel (auch Softwarewerkzeuge)": {"account_type": "Fixed Asset"},
|
||||||
"B\u00fcromaterial und Drucksorten": {},
|
"0540 Hebezeuge und Montageanlagen ": {"account_type": "Fixed Asset"},
|
||||||
"Fachliteratur und Zeitungen ": {},
|
"0550 Geringwertige Vermögensgegenstände, soweit im Erzeugungsprozess ": {"account_type": "Fixed Asset"},
|
||||||
"Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {},
|
"0560 Festwerte technische Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
|
||||||
"Mitgliedsbeitr\u00e4ge": {},
|
"0590 Kumulierte Abschreibungen zu technischen Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
|
||||||
"Skontoertr\u00e4ge auf sonstige betriebliche Aufwendungen": {},
|
"0600 Betriebs- und Geschäftsausstattung, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
|
||||||
"Sonstige betrieblichen Aufwendungen": {},
|
"0610 Andere Anlagen, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
|
||||||
"Spenden und Trinkgelder": {},
|
"0620 Büromaschinen, EDV-Anlagen ": {"account_type": "Fixed Asset"},
|
||||||
"Spesen des Geldverkehrs": {},
|
"0630 PKW und Kombis ": {"account_type": "Fixed Asset"},
|
||||||
"Verluste aus dem Abgang vom Anlageverm\u00f6gen, ausgenommen Finanzanlagen": {},
|
"0640 LKW ": {"account_type": "Fixed Asset"},
|
||||||
"Vertriebskosten": {},
|
"0650 Andere Beförderungsmittel ": {"account_type": "Fixed Asset"},
|
||||||
"Verwaltungskosten": {},
|
"0660 Gebinde ": {"account_type": "Fixed Asset"},
|
||||||
"root_type": "Expense"
|
"0670 Geringwertige Vermögensgegenstände, soweit nicht im Erzeugungssprozess verwendet": {"account_type": "Fixed Asset"},
|
||||||
},
|
"0680 Festwerte außer technische Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
|
||||||
"Summe Betriebliche Ertr\u00e4ge": {
|
"0690 Kumulierte Abschreibungen zu anderen Anlagen, Betriebs- und Geschäftsausstattung": {"account_type": "Fixed Asset"},
|
||||||
"4400 bis 4490 Erl\u00f6sschm\u00e4lerungen": {},
|
"0700 Geleistete Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
|
||||||
"4500 bis 4570 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {},
|
"0710 Anlagen in Bau ": {"account_type": "Fixed Asset"},
|
||||||
"4580 bis 4590 andere aktivierte Eigenleistungen": {},
|
"0790 Kumulierte Abschreibungen zu geleisteten Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
|
||||||
"4600 bis 4620 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
|
"0800 Anteile an verbundenen Unternehmen ": {"account_type": "Fixed Asset"},
|
||||||
"4630 bis 4650 Ertr\u00e4ge aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
|
"0810 Beteiligungen an Gemeinschaftsunternehmen ": {"account_type": "Fixed Asset"},
|
||||||
"4660 bis 4670 Ertr\u00e4ge aus der Zuschreibung zum Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
|
"0820 Beteiligungen an angeschlossenen (assoziierten) Unternehmen ": {"account_type": "Fixed Asset"},
|
||||||
"4700 bis 4790 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {},
|
"0830 Eigene Anteile, Anteile an herrschenden oder mit Mehrheit beteiligten ": {"account_type": "Fixed Asset"},
|
||||||
"4800 bis 4990 \u00dcbrige betriebliche Ertr\u00e4ge": {},
|
"0840 Sonstige Beteiligungen ": {"account_type": "Fixed Asset"},
|
||||||
"Erl\u00f6se 0 % Ausfuhrlieferungen/Drittl\u00e4nder": {},
|
"0850 Ausleihungen an verbundene Unternehmen ": {"account_type": "Fixed Asset"},
|
||||||
"Erl\u00f6se 10 %": {},
|
"0860 Ausleihungen an Unternehmen mit Beteiligungsverhältnis": {"account_type": "Fixed Asset"},
|
||||||
"Erl\u00f6se 20 %": {},
|
"0870 Ausleihungen an Gesellschafter ": {"account_type": "Fixed Asset"},
|
||||||
"Erl\u00f6se aus im Inland stpfl. EG Lieferungen 10 % USt": {},
|
"0880 Sonstige Ausleihungen ": {"account_type": "Fixed Asset"},
|
||||||
"Erl\u00f6se aus im Inland stpfl. EG Lieferungen 20 % USt": {},
|
"0890 Anteile an Kapitalgesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
|
||||||
"Erl\u00f6se i.g. Lieferungen (stfr)": {},
|
"0900 Anteile an Personengesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
|
||||||
"root_type": "Income"
|
"0910 Genossenschaftsanteile ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
|
||||||
},
|
"0920 Anteile an Investmentfonds ": {"account_type": "Fixed Asset"},
|
||||||
"Summe Eigenkapital R\u00fccklagen Abschlusskonten": {
|
"0930 Festverzinsliche Wertpapiere des Anlagevermögens ": {"account_type": "Fixed Asset"},
|
||||||
"9000 bis 9180 Gezeichnetes bzw. gewidmetes Kapital": {
|
"0980 Geleistete Anzahlungen auf Finanzanlagen ": {"account_type": "Fixed Asset"},
|
||||||
"account_type": "Equity"
|
"0990 Kumulierte Abschreibungen zu Finanzanlagen ": {"account_type": "Fixed Asset"},
|
||||||
},
|
"root_type": "Asset"
|
||||||
"9200 bis 9290 Kapitalr\u00fccklagen": {
|
},
|
||||||
"account_type": "Equity"
|
"Klasse 1 Aktiva: Vorr\u00e4te": {
|
||||||
},
|
"1000 Bezugsverrechnung": {"account_type": "Stock"},
|
||||||
"9300 bis 9380 Gewinnr\u00fccklagen": {
|
"1100 Rohstoffe": {"account_type": "Stock"},
|
||||||
"account_type": "Equity"
|
"1200 Bezogene Teile": {"account_type": "Stock"},
|
||||||
},
|
"1300 Hilfsstoffe": {"account_type": "Stock"},
|
||||||
"9400 bis 9590 Bewertungsreserven uns sonst. unversteuerte R\u00fccklagen": {
|
"1350 Betriebsstoffe": {"account_type": "Stock"},
|
||||||
"account_type": "Equity"
|
"1360 Vorrat Energietraeger": {"account_type": "Stock"},
|
||||||
},
|
"1400 Unfertige Erzeugnisse": {"account_type": "Stock"},
|
||||||
"9600 bis 9690 Privat und Verrechnungskonten bei Einzelunternehmen und Personengesellschaften": {},
|
"1500 Fertige Erzeugnisse": {"account_type": "Stock"},
|
||||||
"9700 bis 9790 Einlagen stiller Gesellschafter ": {},
|
"1600 Handelswarenvorrat": {"account_type": "Stock Received But Not Billed"},
|
||||||
"9900 bis 9999 Evidenzkonten": {},
|
"1700 Noch nicht abrechenbare Leistungen": {"account_type": "Stock"},
|
||||||
"Bilanzgewinn (-verlust )": {
|
"1900 Wertberichtigungen": {"account_type": "Stock"},
|
||||||
"account_type": "Equity"
|
"1800 Geleistete Anzahlungen": {"account_type": "Stock"},
|
||||||
},
|
"1900 Wertberichtigungen": {"account_type": "Stock"},
|
||||||
"Er\u00f6ffnungsbilanz": {},
|
"root_type": "Asset"
|
||||||
"Gewinn- und Verlustrechnung": {},
|
},
|
||||||
"Schlussbilanz": {},
|
"Klasse 3 Passiva: Verbindlichkeiten": {
|
||||||
"nicht eingeforderte ausstehende Einlagen": {
|
"3000 Allgemeine Verbindlichkeiten (Schuld)": {"account_type": "Payable"},
|
||||||
"account_type": "Equity"
|
"3010 R\u00fcckstellungen f\u00fcr Pensionen": {"account_type": "Payable"},
|
||||||
},
|
"3020 Steuerr\u00fcckstellungen": {"account_type": "Tax"},
|
||||||
"root_type": "Equity"
|
"3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"},
|
||||||
},
|
"3110 Verbindlichkeiten gegen\u00fcber Bank": {"account_type": "Payable"},
|
||||||
"Summe Finanzertr\u00e4ge und Aufwendungen": {
|
"3150 Verbindlichkeiten Darlehen": {"account_type": "Payable"},
|
||||||
"8000 bis 8040 Ertr\u00e4ge aus Beteiligungen": {},
|
"3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"},
|
||||||
"8050 bis 8090 Ertr\u00e4ge aus anderen Wertpapieren und Ausleihungen des Finanzanlageverm\u00f6gens": {},
|
"3380 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
|
||||||
"8100 bis 8130 Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge": {},
|
|
||||||
"8220 bis 8250 Aufwendungen aus Beteiligungen": {},
|
|
||||||
"8260 bis 8270 Aufwendungen aus sonst. Fiananzanlagen und aus Wertpapieren des Umlaufverm\u00f6gens": {},
|
|
||||||
"8280 bis 8340 Zinsen und \u00e4hnliche Aufwendungem": {},
|
|
||||||
"8400 bis 8440 Au\u00dferordentliche Ertr\u00e4ge": {},
|
|
||||||
"8450 bis 8490 Au\u00dferordentliche Aufwendungen": {},
|
|
||||||
"8500 bis 8590 Steuern vom Einkommen und vom Ertrag": {
|
|
||||||
"account_type": "Tax"
|
|
||||||
},
|
|
||||||
"8600 bis 8690 Aufl\u00f6sung unversteuerten R\u00fccklagen": {},
|
|
||||||
"8700 bis 8740 Aufl\u00f6sung von Kapitalr\u00fccklagen": {},
|
|
||||||
"8750 bis 8790 Aufl\u00f6sung von Gewinnr\u00fccklagen": {},
|
|
||||||
"8800 bis 8890 Zuweisung von unversteuerten R\u00fccklagen": {},
|
|
||||||
"Buchwert abgegangener Beteiligungen": {},
|
|
||||||
"Buchwert abgegangener Wertpapiere des Umlaufverm\u00f6gens": {},
|
|
||||||
"Buchwert abgegangener sonstiger Finanzanlagen": {},
|
|
||||||
"Erl\u00f6se aus dem Abgang von Beteiligungen": {},
|
|
||||||
"Erl\u00f6se aus dem Abgang von Wertpapieren des Umlaufverm\u00f6gens": {},
|
|
||||||
"Erl\u00f6se aus dem Abgang von sonstigen Finanzanlagen": {},
|
|
||||||
"Ertr\u00e4ge aus dem Abgang von und der Zuschreibung zu Finanzanlagen": {},
|
|
||||||
"Ertr\u00e4ge aus dem Abgang von und der Zuschreibung zu Wertpapieren des Umlaufverm\u00f6gens": {},
|
|
||||||
"Gewinabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {},
|
|
||||||
"nicht ausgenutzte Lieferantenskonti": {},
|
|
||||||
"root_type": "Income"
|
|
||||||
},
|
|
||||||
"Summe Fremdkapital": {
|
|
||||||
"3020 bis 3030 Steuerr\u00fcckstellungen": {},
|
|
||||||
"3040 bis 3090 Sonstige R\u00fcckstellungen": {},
|
|
||||||
"3110 bis 3170 Verbindlichkeiten gegen\u00fcber Kredidinstituten": {},
|
|
||||||
"3180 bis 3190 Verbindlichkeiten gegen\u00fcber Finanzinstituten": {},
|
|
||||||
"3380 bis 3390 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
|
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"3400 bis 3470 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
|
"3400 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
|
||||||
"3600 bis 3690 Verbindlichkeiten im Rahmen der sozialen Sicherheit": {},
|
"3460 Verbindlichkeiten gegenueber Gesellschaftern": {"account_type": "Payable"},
|
||||||
"3700 bis 3890 \u00dcbrige sonstige Verbindlichkeiten": {},
|
"3470 Einlagen stiller Gesellschafter": {"account_type": "Payable"},
|
||||||
"3900 bis 3990 Passive Rechnungsabgrenzungsposten": {},
|
"3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"},
|
||||||
"Anleihen (einschlie\u00dflich konvertibler)": {},
|
"3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"},
|
||||||
"Erhaltene Anzahlungenauf Bestellungen": {},
|
"3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"},
|
||||||
"R\u00fcckstellungen f\u00fcr Abfertigung": {},
|
"3600 Verbindlichkeiten Sozialversicherung": {"account_type": "Payable"},
|
||||||
"R\u00fcckstellungen f\u00fcr Pensionen": {},
|
"3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"},
|
||||||
"USt. \u00a719 /art (reverse charge)": {
|
"3700 Sonstige Verbindlichkeiten": {"account_type": "Payable"},
|
||||||
|
"3900 Passive Rechnungsabgrenzungsposten": {"account_type": "Payable"},
|
||||||
|
"3100 Anleihen (einschlie\u00dflich konvertibler)": {"account_type": "Payable"},
|
||||||
|
"3200 Erhaltene Anzahlungen auf Bestellungen": {"account_type": "Payable"},
|
||||||
|
"3040 R\u00fcckstellungen f\u00fcr Abfertigung": {"account_type": "Payable"},
|
||||||
|
|
||||||
|
"3530 USt. \u00a719 (reverse charge)": {
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer": {},
|
"3500 Verbindlichkeiten aus Umsatzsteuer": {"account_type": "Tax"},
|
||||||
"Umsatzsteuer Zahllast": {
|
"3580 Umsatzsteuer Zahllast": {
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer aus i.g. Erwerb 10%": {
|
"3510 Umsatzsteuer Inland 20%": {
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer aus i.g. Erwerb 20%": {
|
"3515 Umsatzsteuer Inland 10%": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"3520 Umsatzsteuer aus i.g. Erwerb 20%": {
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer aus i.g. Lieferungen 10%": {
|
"3525 Umsatzsteuer aus i.g. Erwerb 10%": {
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer aus i.g. Lieferungen 20%": {
|
"3560 Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
|
||||||
"account_type": "Tax"
|
"3360 Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
|
||||||
},
|
|
||||||
"Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
|
|
||||||
"Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
|
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Verbindlichkeiten aus Lieferungen u. Leistungen Inland": {
|
"3000 Verbindlichkeiten aus Lieferungen u. Leistungen Inland": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": {
|
"3370 Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Verbindlichkeiten gegen\u00fcber Gesellschaften": {},
|
"3400 Verbindlichkeiten gegen\u00fcber verbundenen Unternehmen": {},
|
||||||
"Verrechnung Finanzamt": {
|
"3570 Verrechnung Finanzamt": {
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"root_type": "Liability"
|
"root_type": "Liability"
|
||||||
},
|
},
|
||||||
"Summe Kontoklasse 0 Anlageverm\u00f6gen": {
|
"Klasse 2 Aktiva: Umlaufverm\u00f6gen, Rechnungsabgrenzungen": {
|
||||||
"44 bis 49 Sonstige Maschinen und maschinelle Anlagen": {},
|
"2030 Forderungen aus Lieferungen und Leistungen Inland (0% USt, umsatzsteuerfrei)": {
|
||||||
"920 bis 930 Festverzinsliche Wertpapiere des Anlageverm\u00f6gens": {},
|
"account_type": "Receivable"
|
||||||
"940 bis 970 Sonstige Finanzanlagen, Wertrechte": {},
|
},
|
||||||
"Allgemeine Werkzeuge und Handwerkzeuge": {},
|
"2010 Forderungen aus Lieferungen und Leistungen Inland (10% USt, umsatzsteuerfrei)": {
|
||||||
"Andere Bef\u00f6rderungsmittel": {},
|
"account_type": "Receivable"
|
||||||
"Andere Betriebs- und Gesch\u00e4ftsausstattung": {},
|
},
|
||||||
"Andere Erzeugungshilfsmittel": {},
|
"2000 Forderungen aus Lieferungen und Leistungen Inland (20% USt, umsatzsteuerfrei)": {
|
||||||
"Anlagen im Bau": {},
|
"account_type": "Receivable"
|
||||||
"Anteile an Investmentfonds": {},
|
},
|
||||||
"Anteile an Kapitalgesellschaften ohne Beteiligungscharakter": {},
|
"2040 Forderungen aus Lieferungen und Leistungen Inland (sonstiger USt-Satz)": {
|
||||||
"Anteile an Personengesellschaften ohne Beteiligungscharakter": {},
|
"account_type": "Receivable"
|
||||||
"Anteile an verbundenen Unternehmen": {},
|
},
|
||||||
"Antriebsmaschinen": {},
|
"2100 Forderungen aus Lieferungen und Leistungen EU": {
|
||||||
"Aufwendungen f\u00fcs das Ingangssetzen u. Erweitern eines Betriebes": {},
|
"account_type": "Receivable"
|
||||||
"Ausleihungen an verbundene Unternehmen": {},
|
},
|
||||||
"Ausleihungen an verbundene Unternehmen, mit denen ein Beteiligungsverh\u00e4lnis besteht": {},
|
"2150 Forderungen aus Lieferungen und Leistungen Ausland (Nicht-EU)": {
|
||||||
"Bauliche Investitionen in fremden (gepachteten) Betriebs- und Gesch\u00e4ftsgeb\u00e4uden": {},
|
"account_type": "Receivable"
|
||||||
"Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgeb\u00e4uden": {},
|
},
|
||||||
"Bebaute Grundst\u00fccke (Grundwert)": {},
|
"2200 Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||||
"Beheizungs- und Beleuchtungsanlagen": {},
|
"account_type": "Receivable"
|
||||||
"Beteiligungen an Gemeinschaftunternehmen": {},
|
},
|
||||||
"Beteiligungen an angeschlossenen (assoziierten) Unternehmen": {},
|
"2250 Forderungen gegen\u00fcber Unternehmen, mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||||
"Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf eigenem Grund": {},
|
"account_type": "Receivable"
|
||||||
"Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf fremdem Grund": {},
|
},
|
||||||
"B\u00fcromaschinen, EDV - Anlagen": {},
|
"2300 Sonstige Forderungen und Verm\u00f6gensgegenst\u00e4nde": {
|
||||||
"Datenverarbeitungsprogramme": {},
|
"account_type": "Receivable"
|
||||||
"Energieversorgungsanlagen": {},
|
},
|
||||||
"Fertigungsmaschinen": {},
|
"2630 Sonstige Wertpapiere": {
|
||||||
"Gebinde": {},
|
"account_type": "Stock"
|
||||||
"Geleistete Anzahlungen": {},
|
},
|
||||||
"Genossenschaften ohne Beteiligungscharakter": {},
|
"2750 Kassenbest\u00e4nde in Fremdw\u00e4hrung": {
|
||||||
"Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit im Erzeugerprozess verwendet": {},
|
"account_type": "Cash"
|
||||||
"Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit nicht im Erzeugungsprozess verwendet": {},
|
},
|
||||||
"Gesch\u00e4fts(Firmen)wert": {},
|
"2900 Aktive Rechnungsabrenzungsposten": {
|
||||||
"Grundst\u00fcckseinrichtunten auf eigenem Grund": {},
|
"account_type": "Receivable"
|
||||||
"Grundst\u00fcckseinrichtunten auf fremdem Grund": {},
|
},
|
||||||
"Grundst\u00fccksgleiche Rechte": {},
|
"2600 Anteile an verbundenen Unternehmen": {
|
||||||
"Hebezeuge und Montageanlagen": {},
|
"account_type": "Equity"
|
||||||
"Konzessionen": {},
|
},
|
||||||
"Kumulierte Abschreibungen": {},
|
"2680 Besitzwechsel ohne Forderungen": {
|
||||||
"LKW": {},
|
"account_type": "Receivable"
|
||||||
"Marken, Warenzeichen und Musterschutzrechte": {},
|
},
|
||||||
"Maschinenwerkzeuge": {},
|
"2950 Aktiviertes Disagio": {
|
||||||
"Nachrichten- und Kontrollanlagen": {},
|
"account_type": "Receivable"
|
||||||
"PKW": {},
|
},
|
||||||
"Pacht- und Mietrechte": {},
|
"2610 Eigene Anteile und Wertpapiere an mit Mehrheit beteiligten Unternehmen": {
|
||||||
"Patentrechte und Lizenzen": {},
|
"account_type": "Receivable"
|
||||||
"Sonstige Ausleihungen": {},
|
},
|
||||||
"Sonstige Beteiligungen": {},
|
"2570 Einfuhrumsatzsteuer (bezahlt)": {"account_type": "Tax"},
|
||||||
"Transportanlagen": {},
|
|
||||||
"Unbebaute Grundst\u00fccke": {},
|
"2460 Eingeforderte aber noch nicht eingezahlte Einlagen": {
|
||||||
"Vorrichtungen, Formen und Modelle": {},
|
"account_type": "Receivable"
|
||||||
"Wohn- und Sozialgeb\u00e4ude auf eigenem Grund": {},
|
},
|
||||||
"Wohn- und Sozialgeb\u00e4ude auf fremdem Grund": {},
|
"2180 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2130 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2080 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2270 Einzelwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2230 Einzelwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2470 Einzelwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2700 Kassenbestand": {
|
||||||
|
"account_type": "Cash"
|
||||||
|
},
|
||||||
|
"2190 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. sonstiges Ausland": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2130 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2100 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2280 Pauschalwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2240 Pauschalwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2480 Pauschalwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2740 Postwertzeichen": {
|
||||||
|
"account_type": "Cash"
|
||||||
|
},
|
||||||
|
"2780 Schecks in Euro": {
|
||||||
|
"account_type": "Cash"
|
||||||
|
},
|
||||||
|
"2800 Guthaben bei Bank": {
|
||||||
|
"account_type": "Bank"
|
||||||
|
},
|
||||||
|
"2801 Guthaben bei Bank - Sparkonto": {
|
||||||
|
"account_type": "Bank"
|
||||||
|
},
|
||||||
|
"2810 Guthaben bei Paypal": {
|
||||||
|
"account_type": "Bank"
|
||||||
|
},
|
||||||
|
"2930 Mietvorauszahlungen": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2980 Abgrenzung latenter Steuern": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2500 Vorsteuer": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
|
"2510 Vorsteuer Inland 10%": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"2895 Schwebende Geldbewegugen": {
|
||||||
|
"account_type": "Bank"
|
||||||
|
},
|
||||||
|
"2513 Vorsteuer Inland 5%": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"2515 Vorsteuer Inland 20%": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"2520 Vorsteuer aus innergemeinschaftlichem Erwerb 10%": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"2525 Vorsteuer aus innergemeinschaftlichem Erwerb 20%": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"2530 Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"2690 Wertberichtigungen zu Wertpapieren und Anteilen": {
|
||||||
|
"account_type": "Receivable"
|
||||||
|
},
|
||||||
"root_type": "Asset"
|
"root_type": "Asset"
|
||||||
},
|
},
|
||||||
"Summe Personalaufwand": {
|
"Klasse 4: Betriebliche Erträge": {
|
||||||
"6000 bis 6190 L\u00f6hne": {},
|
"4000 Erlöse 20 %": {"account_type": "Income Account"},
|
||||||
"6200 bis 6390 Geh\u00e4lter": {},
|
"4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"},
|
||||||
"6400 bis 6440 Aufwendungen f\u00fcr Abfertigungen": {},
|
"4010 Erl\u00f6se 10 %": {"account_type": "Income Account"},
|
||||||
"6450 bis 6490 Aufwendungen f\u00fcr Altersversorgung": {},
|
"4030 Erl\u00f6se 13 %": {"account_type": "Income Account"},
|
||||||
"6500 bis 6550 Gesetzlicher Sozialaufwand Arbeiter": {},
|
"4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"},
|
||||||
"6560 bis 6590 Gesetzlicher Sozialaufwand Angestellte": {},
|
"4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"},
|
||||||
"6600 bis 6650 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {},
|
"4410 Erl\u00f6sreduktion 10 %": {"account_type": "Expense Account"},
|
||||||
"6660 bis 6690 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {},
|
"4420 Erl\u00f6sreduktion 20 %": {"account_type": "Expense Account"},
|
||||||
"6700 bis 6890 Sonstige Sozialaufwendungen": {},
|
"4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"},
|
||||||
"Aufwandsstellenrechnung": {},
|
"4440 Erl\u00f6sreduktion 0 % innergemeinschaftliche Lieferungen": {"account_type": "Expense Account"},
|
||||||
|
"4500 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {"account_type": "Income Account"},
|
||||||
|
"4580 Aktivierte Eigenleistungen": {"account_type": "Income Account"},
|
||||||
|
"4600 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
|
||||||
|
"4630 Ertr\u00e4ge aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
|
||||||
|
"4660 Ertr\u00e4ge aus der Zuschreibung zum Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
|
||||||
|
"4700 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {"account_type": "Income Account"},
|
||||||
|
"4800 \u00dcbrige betriebliche Ertr\u00e4ge": {"account_type": "Income Account"},
|
||||||
|
"root_type": "Income"
|
||||||
|
},
|
||||||
|
"Klasse 5: Aufwand f\u00fcr Material und Leistungen": {
|
||||||
|
"5000 Einkauf Partnerleistungen": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5100 Verbrauch an Rohstoffen": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5200 Verbrauch von bezogenen Fertig- und Einzelteilen": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5300 Verbrauch von Hilfsstoffen": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5340 Verbrauch Verpackungsmaterial": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5470 Verbrauch von Kleinmaterial": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5450 Verbrauch von Reinigungsmaterial": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5400 Verbrauch von Betriebsstoffen": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5500 Verbrauch von Werkzeugen und anderen Erzeugungshilfsmittel": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5600 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5700 Bearbeitung durch Dritte": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5900 Aufwandsstellenrechnung Material": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5820 Skontoertr\u00e4ge (20% USt.)": {"account_type": "Income Account"},
|
||||||
|
"5810 Skontoertr\u00e4ge (10% USt.)": {"account_type": "Income Account"},
|
||||||
|
"5010 Handelswareneinkauf 10 %": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5020 Handelswareneinkauf 20 %": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5040 Handelswareneinkauf innergemeinschaftlicher Erwerb 10 % VSt/10 % USt": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5050 Handelswareneinkauf innergemeinschaftlicher Erwerb 20 % VSt/20 % USt": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5070 Handelswareneinkauf innergemeinschaftlicher Erwerb ohne Vorsteuerabzug und 10 % USt": {"account_type": "Cost of Goods Sold"},
|
||||||
|
"5080 Handelswareneinkauf innergemeinschaftlicher Erwerb ohne Vorsteuerabzug und 20 % USt": {"account_type": "Cost of Goods Sold"},
|
||||||
"root_type": "Expense"
|
"root_type": "Expense"
|
||||||
},
|
},
|
||||||
"Summe Umlaufverm\u00f6gen": {
|
"Klasse 6: Personalaufwand": {
|
||||||
"2000 bis 2007 Forderungen aus Lief. und Leist. Inland": {
|
"6000 L\u00f6hne": {"account_type": "Payable"},
|
||||||
"account_type": "Receivable"
|
"6200 Geh\u00e4lter": {"account_type": "Payable"},
|
||||||
},
|
"6400 Aufwendungen f\u00fcr Abfertigungen": {"account_type": "Payable"},
|
||||||
"2100 bis 2120 Forderungen aus Lief. und Leist. EU": {
|
"6450 Aufwendungen f\u00fcr Altersversorgung": {"account_type": "Payable"},
|
||||||
"account_type": "Receivable"
|
"6500 Gesetzlicher Sozialaufwand Arbeiter": {"account_type": "Payable"},
|
||||||
},
|
"6560 Gesetzlicher Sozialaufwand Angestellte": {"account_type": "Payable"},
|
||||||
"2150 bis 2170 Forderungen aus Lief. und Leist. Ausland": {
|
"6600 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
|
||||||
"account_type": "Receivable"
|
"6660 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
|
||||||
},
|
"6700 Sonstige Sozialaufwendungen": {"account_type": "Payable"},
|
||||||
"2200 bis 2220 Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
"6900 Aufwandsstellenrechnung Personal": {"account_type": "Payable"},
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"2250 bis 2270 Forderungen gegen\u00fcber Unternehmen, mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"2300 bis 2460 Sonstige Forderungen und Verm\u00f6gensgegenst\u00e4nde": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"2630 bis 2670 Sonstige Wertpapiere": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"2750 bis 2770 Kassenbest\u00e4nde in Fremdw\u00e4hrung": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Aktive Rechnungsabrenzungsposten": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Anteile an verbundenen Unternehmen": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Bank / Guthaben bei Kreditinstituten": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Besitzwechsel ...": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Disagio": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Eigene Anteile (Wertpapiere)": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Einfuhrumsatzsteuer (bezahlt)": {},
|
|
||||||
"Eingeforderte aber noch nicht eingezahlte Einlagen": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Einzelwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Einzelwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Einzelwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Kassenbestand": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Pauschalwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Pauschalwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Pauschalwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Postwertzeichen": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Schecks in Inlandsw\u00e4hrung": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Sonstige Anteile": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Stempelmarken": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Steuerabgrenzung": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Unterschiedsbetrag gem. Abschnitt XII Pensionskassengesetz": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Unterschiedsbetrag zur gebotenen Pensionsr\u00fcckstellung": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Vorsteuer": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"Vorsteuer aus ig. Erwerb 10%": {
|
|
||||||
"account_type": "Tax"
|
|
||||||
},
|
|
||||||
"Vorsteuer aus ig. Erwerb 20%": {
|
|
||||||
"account_type": "Tax"
|
|
||||||
},
|
|
||||||
"Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
|
|
||||||
"account_type": "Tax"
|
|
||||||
},
|
|
||||||
"Wertberichtigungen": {
|
|
||||||
"account_type": "Receivable"
|
|
||||||
},
|
|
||||||
"root_type": "Asset"
|
|
||||||
},
|
|
||||||
"Summe Vorr\u00e4te": {
|
|
||||||
"1000 bis 1090 Bezugsverrechnung": {},
|
|
||||||
"1100 bis 1190 Rohstoffe": {},
|
|
||||||
"1200 bis 1290 Bezogene Teile": {},
|
|
||||||
"1300 bis 1340 Hilfsstoffe": {},
|
|
||||||
"1350 bis 1390 Betriebsstoffe": {},
|
|
||||||
"1400 bis 1490 Unfertige Erzeugniss": {},
|
|
||||||
"1500 bis 1590 Fertige Erzeugniss": {},
|
|
||||||
"1600 bis 1690 Waren": {},
|
|
||||||
"1700 bis 1790 Noch nicht abgerechenbare Leistungen": {},
|
|
||||||
"1900 bis 1990 Wertberichtigungen": {},
|
|
||||||
"geleistete Anzahlungen": {},
|
|
||||||
"root_type": "Asset"
|
|
||||||
},
|
|
||||||
"Summe Wareneinsatz": {
|
|
||||||
"5100 bis 5190 Verbrauch an Rohstoffen": {},
|
|
||||||
"5200 bis 5290 Verbrauch von bezogenen Fertig- und Einzelteilen": {},
|
|
||||||
"5300 bis 5390 Verbrauch von Hilfsstoffen": {},
|
|
||||||
"5400 bis 5490 Verbrauch von Betriebsstoffen": {},
|
|
||||||
"5500 bis 5590 Verbrauch von Werkzeugen und anderen Erzeugungshilfsmittel": {},
|
|
||||||
"5600 bis 5690 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {},
|
|
||||||
"5700 bis 5790 Sonstige bezogene Herstellungsleistungen": {},
|
|
||||||
"Aufwandsstellenrechnung": {},
|
|
||||||
"Skontoertr\u00e4ge auf Materialaufwand": {},
|
|
||||||
"Skontoertr\u00e4ge auf sonstige bezogene Herstellungsleistungen": {},
|
|
||||||
"Wareneinkauf 10 %": {},
|
|
||||||
"Wareneinkauf 20 %": {},
|
|
||||||
"Wareneinkauf igErwerb 10 % VSt/10 % USt": {},
|
|
||||||
"Wareneinkauf igErwerb 20 % VSt/20 % USt": {},
|
|
||||||
"Wareneinkauf igErwerb ohne Vorsteuerabzug und 10 % USt": {},
|
|
||||||
"Wareneinkauf igErwerb ohne Vorsteuerabzug und 20 % USt": {},
|
|
||||||
"root_type": "Expense"
|
"root_type": "Expense"
|
||||||
|
},
|
||||||
|
"Klasse 7: Abschreibungen und sonstige betriebliche Aufwendungen": {
|
||||||
|
"7010 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {"account_type": "Depreciation"},
|
||||||
|
"7100 Sonstige Steuern und Geb\u00fchren": {"account_type": "Tax"},
|
||||||
|
"7200 Instandhaltung u. Reinigung durch Dritte, Entsorgung, Energie": {"account_type": "Expense Account"},
|
||||||
|
"7300 Transporte durch Dritte": {"account_type": "Expense Account"},
|
||||||
|
"7310 Fahrrad - Aufwand": {"account_type": "Expense Account"},
|
||||||
|
"7320 Kfz - Aufwand": {"account_type": "Expense Account"},
|
||||||
|
"7330 LKW - Aufwand": {"account_type": "Expense Account"},
|
||||||
|
"7340 Lastenrad - Aufwand": {"account_type": "Expense Account"},
|
||||||
|
"7350 Reise- und Fahraufwand": {"account_type": "Expense Account"},
|
||||||
|
"7360 Tag- und N\u00e4chtigungsgelder": {"account_type": "Expense Account"},
|
||||||
|
"7380 Nachrichtenaufwand": {"account_type": "Expense Account"},
|
||||||
|
"7400 Miet- und Pachtaufwand": {"account_type": "Expense Account"},
|
||||||
|
"7440 Leasingaufwand": {"account_type": "Expense Account"},
|
||||||
|
"7480 Lizenzaufwand": {"account_type": "Expense Account"},
|
||||||
|
"7500 Aufwand f\u00fcr beigestelltes Personal": {"account_type": "Expense Account"},
|
||||||
|
"7540 Provisionen an Dritte": {"account_type": "Expense Account"},
|
||||||
|
"7580 Aufsichtsratsverg\u00fctungen": {"account_type": "Expense Account"},
|
||||||
|
"7610 Druckerzeugnisse und Vervielf\u00e4ltigungen": {"account_type": "Expense Account"},
|
||||||
|
"7650 Werbung und Repr\u00e4sentationen": {"account_type": "Expense Account"},
|
||||||
|
"7700 Versicherungen": {"account_type": "Expense Account"},
|
||||||
|
"7750 Beratungs- und Pr\u00fcfungsaufwand": {"account_type": "Expense Account"},
|
||||||
|
"7800 Forderungsverluste und Schadensf\u00e4lle": {"account_type": "Expense Account"},
|
||||||
|
"7840 Verschiedene betriebliche Aufwendungen": {"account_type": "Expense Account"},
|
||||||
|
"7910 Aufwandsstellenrechung der Hersteller": {"account_type": "Expense Account"},
|
||||||
|
"7060 Sofortabschreibungen geringwertig": {"account_type": "Expense Account"},
|
||||||
|
"7070 Abschreibungen vom Umlaufverm\u00f6gen, soweit diese die im Unternehmen \u00fcblichen Abschreibungen \u00fcbersteigen": {"account_type": "Depreciation"},
|
||||||
|
"7900 Aufwandsstellenrechnung": {"account_type": "Expense Account"},
|
||||||
|
"7770 Aus- und Fortbildung": {"account_type": "Expense Account"},
|
||||||
|
"7820 Buchwert abgegangener Anlagen, ausgenommen Finanzanlagen": {"account_type": "Expense Account"},
|
||||||
|
"7600 B\u00fcromaterial und Drucksorten": {"account_type": "Expense Account"},
|
||||||
|
"7630 Fachliteratur und Zeitungen ": {"account_type": "Expense Account"},
|
||||||
|
"7960 Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {"account_type": "Expense Account"},
|
||||||
|
"7780 Mitgliedsbeitr\u00e4ge": {"account_type": "Expense Account"},
|
||||||
|
"7880 Skontoertr\u00e4ge auf sonstige betriebliche Aufwendungen": {"account_type": "Expense Account"},
|
||||||
|
"7990 Sonstige betrieblichen Aufwendungen": {"account_type": "Expense Account"},
|
||||||
|
"7680 Spenden und Trinkgelder": {"account_type": "Expense Account"},
|
||||||
|
"7790 Spesen des Geldverkehrs": {"account_type": "Expense Account"},
|
||||||
|
"7830 Verluste aus dem Abgang vom Anlageverm\u00f6gen, ausgenommen Finanzanlagen": {"account_type": "Expense Account"},
|
||||||
|
"7970 Vertriebskosten": {"account_type": "Expense Account"},
|
||||||
|
"7980 Verwaltungskosten": {"account_type": "Expense Account"},
|
||||||
|
"root_type": "Expense"
|
||||||
|
},
|
||||||
|
"Klasse 8: Finanz- und ausserordentliche Ertr\u00e4ge und Aufwendungen": {
|
||||||
|
"8000 Ertr\u00e4ge aus Beteiligungen": {"account_type": "Income Account"},
|
||||||
|
"8050 Ertr\u00e4ge aus anderen Wertpapieren und Ausleihungen des Finanzanlageverm\u00f6gens": {"account_type": "Income Account"},
|
||||||
|
"8100 Zinsen aus Bankguthaben": {"account_type": "Income Account"},
|
||||||
|
"8110 Zinsen aus gewaehrten Darlehen": {"account_type": "Income Account"},
|
||||||
|
"8130 Verzugszinsenertraege": {"account_type": "Income Account"},
|
||||||
|
"8220 Aufwendungen aus Beteiligungen": {"account_type": "Expense Account"},
|
||||||
|
"8260 Aufwendungen aus sonst. Fiananzanlagen und aus Wertpapieren des Umlaufverm\u00f6gens": {},
|
||||||
|
"8280 Zinsen und \u00e4hnliche Aufwendungem": {"account_type": "Expense Account"},
|
||||||
|
"8400 Au\u00dferordentliche Ertr\u00e4ge": {"account_type": "Income Account"},
|
||||||
|
"8450 Au\u00dferordentliche Aufwendungen": {"account_type": "Expense Account"},
|
||||||
|
"8500 Steuern vom Einkommen und vom Ertrag": {
|
||||||
|
"account_type": "Tax"
|
||||||
|
},
|
||||||
|
"8600 Aufl\u00f6sung unversteuerten R\u00fccklagen": {"account_type": "Income Account"},
|
||||||
|
"8700 Aufl\u00f6sung von Kapitalr\u00fccklagen": {"account_type": "Income Account"},
|
||||||
|
"8750 Aufl\u00f6sung von Gewinnr\u00fccklagen": {"account_type": "Income Account"},
|
||||||
|
"8800 Zuweisung zu unversteuerten R\u00fccklagen": {"account_type": "Expense Account"},
|
||||||
|
"8900 Zuweisung zu Gewinnr\u00fccklagen": {"account_type": "Expense Account"},
|
||||||
|
"8100 Buchwert abgegangener Beteiligungen": {"account_type": "Expense Account"},
|
||||||
|
"8130 Buchwert abgegangener Wertpapiere des Umlaufverm\u00f6gens": {"account_type": "Expense Account"},
|
||||||
|
"8120 Buchwert abgegangener sonstiger Finanzanlagen": {"account_type": "Expense Account"},
|
||||||
|
"8990 Gewinnabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {"account_type": "Expense Account"},
|
||||||
|
"8350 nicht ausgenutzte Lieferantenskonti": {"account_type": "Expense Account"},
|
||||||
|
"root_type": "Income"
|
||||||
|
},
|
||||||
|
"Klasse 9 Passiva: Eigenkapital, R\u00fccklagen, stille Einlagen, Abschlusskonten": {
|
||||||
|
"9000 Gezeichnetes bzw. gewidmetes Kapital": {
|
||||||
|
"account_type": "Equity"
|
||||||
|
},
|
||||||
|
"9200 Kapitalr\u00fccklagen": {
|
||||||
|
"account_type": "Equity"
|
||||||
|
},
|
||||||
|
"9300 Gewinnr\u00fccklagen": {
|
||||||
|
"account_type": "Equity"
|
||||||
|
},
|
||||||
|
"9400 Bewertungsreserven uns sonst. unversteuerte R\u00fccklagen": {
|
||||||
|
"account_type": "Equity"
|
||||||
|
},
|
||||||
|
"9600 Private Entnahmen": {"account_type": "Equity"},
|
||||||
|
"9610 Privatsteuern": {"account_type": "Equity"},
|
||||||
|
"9700 Einlagen stiller Gesellschafter ": {"account_type": "Equity"},
|
||||||
|
"9900 Evidenzkonto": {"account_type": "Equity"},
|
||||||
|
"9800 Er\u00f6ffnungsbilanzkonto (EBK)": {"account_type": "Equity"},
|
||||||
|
"9880 Jahresergebnis laut Gewinn- und Verlustrechnung (G+V)": {"account_type": "Equity"},
|
||||||
|
"9850 Schlussbilanzkonto (SBK)": {"account_type": "Round Off"},
|
||||||
|
"9190 nicht eingeforderte ausstehende Einlagen und berechtigte Entnahmen von Gesellschaftern": {
|
||||||
|
"account_type": "Equity"
|
||||||
|
},
|
||||||
|
"root_type": "Equity"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
{
|
{
|
||||||
"country_code": "de",
|
"country_code": "de",
|
||||||
"name": "SKR03 mit Kontonummern",
|
"name": "SKR03 mit Kontonummern",
|
||||||
"tree": {
|
"tree": {
|
||||||
"Aktiva": {
|
"Aktiva": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"root_type": "Asset",
|
"root_type": "Asset",
|
||||||
"A - Anlagevermögen": {
|
"A - Anlagevermögen": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"EDV-Software": {
|
"EDV-Software": {
|
||||||
"account_number": "0027",
|
"account_number": "0027",
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Gesch\u00e4ftsausstattung": {
|
"Geschäftsausstattung": {
|
||||||
"account_number": "0410",
|
"account_number": "0410",
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"B\u00fcroeinrichtung": {
|
"Büroeinrichtung": {
|
||||||
"account_number": "0420",
|
"account_number": "0420",
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Darlehen": {
|
"Darlehen": {
|
||||||
"account_number": "0565"
|
"account_number": "0565"
|
||||||
},
|
},
|
||||||
"Maschinen": {
|
"Maschinen": {
|
||||||
"account_number": "0210",
|
"account_number": "0210",
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Betriebsausstattung": {
|
"Betriebsausstattung": {
|
||||||
"account_number": "0400",
|
"account_number": "0400",
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Ladeneinrichtung": {
|
"Ladeneinrichtung": {
|
||||||
"account_number": "0430",
|
"account_number": "0430",
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Accumulated Depreciation": {
|
"Accumulated Depreciation": {
|
||||||
"account_type": "Accumulated Depreciation"
|
"account_type": "Accumulated Depreciation"
|
||||||
@@ -60,36 +60,46 @@
|
|||||||
"Durchlaufende Posten": {
|
"Durchlaufende Posten": {
|
||||||
"account_number": "1590"
|
"account_number": "1590"
|
||||||
},
|
},
|
||||||
"Gewinnermittlung \u00a74/3 nicht Ergebniswirksam": {
|
"Verrechnungskonto Gewinnermittlung § 4 Abs. 3 EStG, nicht ergebniswirksam": {
|
||||||
"account_number": "1371"
|
"account_number": "1371"
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer": {
|
"Abziehbare Vorsteuer": {
|
||||||
"account_type": "Tax",
|
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Abziehbare Vorsteuer 7%": {
|
"Abziehbare Vorsteuer 7 %": {
|
||||||
"account_number": "1571"
|
"account_number": "1571",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 7.0
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer 19%": {
|
"Abziehbare Vorsteuer 19 %": {
|
||||||
"account_number": "1576"
|
"account_number": "1576",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 19.0
|
||||||
},
|
},
|
||||||
"Abziehbare Vorsteuer nach \u00a713b UStG 19%": {
|
"Abziehbare Vorsteuer nach § 13b UStG 19 %": {
|
||||||
"account_number": "1577"
|
"account_number": "1577",
|
||||||
},
|
"account_type": "Tax",
|
||||||
"Leistungen \u00a713b UStG 19% Vorsteuer, 19% Umsatzsteuer": {
|
"tax_rate": 19.0
|
||||||
"account_number": "3120"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"III. Wertpapiere": {
|
"III. Wertpapiere": {
|
||||||
"is_group": 1
|
"is_group": 1,
|
||||||
|
"Anteile an verbundenen Unternehmen (Umlaufvermögen)": {
|
||||||
|
"account_number": "1340"
|
||||||
|
},
|
||||||
|
"Anteile an herrschender oder mit Mehrheit beteiligter Gesellschaft": {
|
||||||
|
"account_number": "1344"
|
||||||
|
},
|
||||||
|
"Sonstige Wertpapiere": {
|
||||||
|
"account_number": "1348"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"IV. Kassenbestand, Bundesbankguthaben, Guthaben bei Kreditinstituten und Schecks.": {
|
"IV. Kassenbestand, Bundesbankguthaben, Guthaben bei Kreditinstituten und Schecks.": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Kasse": {
|
"Kasse": {
|
||||||
"account_type": "Cash",
|
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
|
"account_type": "Cash",
|
||||||
"Kasse": {
|
"Kasse": {
|
||||||
"is_group": 1,
|
|
||||||
"account_number": "1000",
|
"account_number": "1000",
|
||||||
"account_type": "Cash"
|
"account_type": "Cash"
|
||||||
}
|
}
|
||||||
@@ -111,21 +121,21 @@
|
|||||||
"C - Rechnungsabgrenzungsposten": {
|
"C - Rechnungsabgrenzungsposten": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Aktive Rechnungsabgrenzung": {
|
"Aktive Rechnungsabgrenzung": {
|
||||||
"account_number": "0980"
|
"account_number": "0980"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"D - Aktive latente Steuern": {
|
"D - Aktive latente Steuern": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Aktive latente Steuern": {
|
"Aktive latente Steuern": {
|
||||||
"account_number": "0983"
|
"account_number": "0983"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"E - Aktiver Unterschiedsbetrag aus der Vermögensverrechnung": {
|
"E - Aktiver Unterschiedsbetrag aus der Vermögensverrechnung": {
|
||||||
"is_group": 1
|
"is_group": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Passiva": {
|
"Passiva": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"root_type": "Liability",
|
"root_type": "Liability",
|
||||||
"A. Eigenkapital": {
|
"A. Eigenkapital": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
@@ -200,26 +210,32 @@
|
|||||||
},
|
},
|
||||||
"Umsatzsteuer": {
|
"Umsatzsteuer": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"account_type": "Tax",
|
"Umsatzsteuer 7 %": {
|
||||||
"Umsatzsteuer 7%": {
|
"account_number": "1771",
|
||||||
"account_number": "1771"
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 7.0
|
||||||
},
|
},
|
||||||
"Umsatzsteuer 19%": {
|
"Umsatzsteuer 19 %": {
|
||||||
"account_number": "1776"
|
"account_number": "1776",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 19.0
|
||||||
},
|
},
|
||||||
"Umsatzsteuer-Vorauszahlung": {
|
"Umsatzsteuer-Vorauszahlung": {
|
||||||
"account_number": "1780"
|
"account_number": "1780",
|
||||||
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer-Vorauszahlung 1/11": {
|
"Umsatzsteuer-Vorauszahlung 1/11": {
|
||||||
"account_number": "1781"
|
"account_number": "1781"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer \u00a7 13b UStG 19%": {
|
"Umsatzsteuer nach § 13b UStG 19 %": {
|
||||||
"account_number": "1787"
|
"account_number": "1787",
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": 19.0
|
||||||
},
|
},
|
||||||
"Umsatzsteuer Vorjahr": {
|
"Umsatzsteuer Vorjahr": {
|
||||||
"account_number": "1790"
|
"account_number": "1790"
|
||||||
},
|
},
|
||||||
"Umsatzsteuer fr\u00fchere Jahre": {
|
"Umsatzsteuer frühere Jahre": {
|
||||||
"account_number": "1791"
|
"account_number": "1791"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,44 +250,56 @@
|
|||||||
"E. Passive latente Steuern": {
|
"E. Passive latente Steuern": {
|
||||||
"is_group": 1
|
"is_group": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Erl\u00f6se u. Ertr\u00e4ge 2/8": {
|
"Erlöse u. Erträge 2/8": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"root_type": "Income",
|
"root_type": "Income",
|
||||||
"Erl\u00f6skonten 8": {
|
"Erlöskonten 8": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Erl\u00f6se": {
|
"Erlöse": {
|
||||||
"account_number": "8200",
|
"account_number": "8200",
|
||||||
"account_type": "Income Account"
|
"account_type": "Income Account"
|
||||||
},
|
},
|
||||||
"Erl\u00f6se USt. 19%": {
|
"Erlöse USt. 19 %": {
|
||||||
"account_number": "8400",
|
"account_number": "8400",
|
||||||
"account_type": "Income Account"
|
"account_type": "Income Account"
|
||||||
},
|
},
|
||||||
"Erl\u00f6se USt. 7%": {
|
"Erlöse USt. 7 %": {
|
||||||
"account_number": "8300",
|
"account_number": "8300",
|
||||||
"account_type": "Income Account"
|
"account_type": "Income Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Ertragskonten 2": {
|
"Ertragskonten 2": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge": {
|
"sonstige Zinsen und ähnliche Erträge": {
|
||||||
"account_number": "2650",
|
"account_number": "2650",
|
||||||
"account_type": "Income Account"
|
"account_type": "Income Account"
|
||||||
},
|
},
|
||||||
"Au\u00dferordentliche Ertr\u00e4ge": {
|
"Außerordentliche Erträge": {
|
||||||
"account_number": "2500",
|
"account_number": "2500",
|
||||||
"account_type": "Income Account"
|
"account_type": "Income Account"
|
||||||
},
|
},
|
||||||
"Sonstige Ertr\u00e4ge": {
|
"Sonstige Erträge": {
|
||||||
"account_number": "2700",
|
"account_number": "2700",
|
||||||
"account_type": "Income Account"
|
"account_type": "Income Account"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Aufwendungen 2/4": {
|
"Aufwendungen 2/4": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"root_type": "Expense",
|
"root_type": "Expense",
|
||||||
|
"Fremdleistungen": {
|
||||||
|
"account_number": "3100",
|
||||||
|
"account_type": "Expense Account"
|
||||||
|
},
|
||||||
|
"Fremdleistungen ohne Vorsteuer": {
|
||||||
|
"account_number": "3109",
|
||||||
|
"account_type": "Expense Account"
|
||||||
|
},
|
||||||
|
"Bauleistungen eines im Inland ansässigen Unternehmers 19 % Vorsteuer und 19 % Umsatzsteuer": {
|
||||||
|
"account_number": "3120",
|
||||||
|
"account_type": "Expense Account"
|
||||||
|
},
|
||||||
"Wareneingang": {
|
"Wareneingang": {
|
||||||
"account_number": "3200"
|
"account_number": "3200"
|
||||||
},
|
},
|
||||||
@@ -298,234 +326,234 @@
|
|||||||
"Gegenkonto 4996-4998": {
|
"Gegenkonto 4996-4998": {
|
||||||
"account_number": "4999"
|
"account_number": "4999"
|
||||||
},
|
},
|
||||||
"Abschreibungen": {
|
"Abschreibungen": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Abschreibungen auf Sachanlagen (ohne AfA auf Kfz und Gebäude)": {
|
"Abschreibungen auf Sachanlagen (ohne AfA auf Kfz und Gebäude)": {
|
||||||
"account_number": "4830",
|
"account_number": "4830",
|
||||||
"account_type": "Accumulated Depreciation"
|
"account_type": "Accumulated Depreciation"
|
||||||
},
|
},
|
||||||
"Abschreibungen auf Gebäude": {
|
"Abschreibungen auf Gebäude": {
|
||||||
"account_number": "4831",
|
"account_number": "4831",
|
||||||
"account_type": "Depreciation"
|
"account_type": "Depreciation"
|
||||||
},
|
},
|
||||||
"Abschreibungen auf Kfz": {
|
"Abschreibungen auf Kfz": {
|
||||||
"account_number": "4832",
|
"account_number": "4832",
|
||||||
"account_type": "Depreciation"
|
"account_type": "Depreciation"
|
||||||
},
|
},
|
||||||
"Sofortabschreibung GWG": {
|
"Sofortabschreibung GWG": {
|
||||||
"account_number": "4855",
|
"account_number": "4855",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Kfz-Kosten": {
|
"Kfz-Kosten": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Kfz-Steuer": {
|
"Kfz-Steuer": {
|
||||||
"account_number": "4510",
|
"account_number": "4510",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Kfz-Versicherungen": {
|
"Kfz-Versicherungen": {
|
||||||
"account_number": "4520",
|
"account_number": "4520",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"laufende Kfz-Betriebskosten": {
|
"laufende Kfz-Betriebskosten": {
|
||||||
"account_number": "4530",
|
"account_number": "4530",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Kfz-Reparaturen": {
|
"Kfz-Reparaturen": {
|
||||||
"account_number": "4540",
|
"account_number": "4540",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Fremdfahrzeuge": {
|
"Fremdfahrzeuge": {
|
||||||
"account_number": "4570",
|
"account_number": "4570",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"sonstige Kfz-Kosten": {
|
"sonstige Kfz-Kosten": {
|
||||||
"account_number": "4580",
|
"account_number": "4580",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Personalkosten": {
|
"Personalkosten": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Geh\u00e4lter": {
|
"Gehälter": {
|
||||||
"account_number": "4120",
|
"account_number": "4120",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"gesetzliche soziale Aufwendungen": {
|
"gesetzliche soziale Aufwendungen": {
|
||||||
"account_number": "4130",
|
"account_number": "4130",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Aufwendungen f\u00fcr Altersvorsorge": {
|
"Aufwendungen für Altersvorsorge": {
|
||||||
"account_number": "4165",
|
"account_number": "4165",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Verm\u00f6genswirksame Leistungen": {
|
"Vermögenswirksame Leistungen": {
|
||||||
"account_number": "4170",
|
"account_number": "4170",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Aushilfsl\u00f6hne": {
|
"Aushilfslöhne": {
|
||||||
"account_number": "4190",
|
"account_number": "4190",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Raumkosten": {
|
"Raumkosten": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Miete und Nebenkosten": {
|
"Miete und Nebenkosten": {
|
||||||
"account_number": "4210",
|
"account_number": "4210",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Gas, Wasser, Strom (Verwaltung, Vertrieb)": {
|
"Gas, Wasser, Strom (Verwaltung, Vertrieb)": {
|
||||||
"account_number": "4240",
|
"account_number": "4240",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Reinigung": {
|
"Reinigung": {
|
||||||
"account_number": "4250",
|
"account_number": "4250",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Reparatur/Instandhaltung": {
|
"Reparatur/Instandhaltung": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Reparatur u. Instandh. von Anlagen/Maschinen u. Betriebs- u. Gesch\u00e4ftsausst.": {
|
"Reparaturen und Instandhaltungen von anderen Anlagen und Betriebs- und Geschäftsausstattung": {
|
||||||
"account_number": "4805",
|
"account_number": "4805",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Versicherungsbeitr\u00e4ge": {
|
"Versicherungsbeiträge": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Versicherungen": {
|
"Versicherungen": {
|
||||||
"account_number": "4360",
|
"account_number": "4360",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Beitr\u00e4ge": {
|
"Beiträge": {
|
||||||
"account_number": "4380",
|
"account_number": "4380",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"sonstige Ausgaben": {
|
"sonstige Ausgaben": {
|
||||||
"account_number": "4390",
|
"account_number": "4390",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
|
"steuerlich abzugsfähige Verspätungszuschläge und Zwangsgelder": {
|
||||||
"account_number": "4396",
|
"account_number": "4396",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Werbe-/Reisekosten": {
|
"Werbe-/Reisekosten": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Werbekosten": {
|
"Werbekosten": {
|
||||||
"account_number": "4610",
|
"account_number": "4610",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Aufmerksamkeiten": {
|
"Aufmerksamkeiten": {
|
||||||
"account_number": "4653",
|
"account_number": "4653",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"nicht abzugsf\u00e4hige Betriebsausg. aus Werbe-, Repr\u00e4s.- u. Reisekosten": {
|
"nicht abzugsfähige Betriebsausg. aus Werbe-, Repräs.- u. Reisekosten": {
|
||||||
"account_number": "4665",
|
"account_number": "4665",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Reisekosten Unternehmer": {
|
"Reisekosten Unternehmer": {
|
||||||
"account_number": "4670",
|
"account_number": "4670",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"verschiedene Kosten": {
|
"verschiedene Kosten": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Porto": {
|
"Porto": {
|
||||||
"account_number": "4910",
|
"account_number": "4910",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Telekom": {
|
"Telekom": {
|
||||||
"account_number": "4920",
|
"account_number": "4920",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Mobilfunk D2": {
|
"Mobilfunk D2": {
|
||||||
"account_number": "4921",
|
"account_number": "4921",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Internet": {
|
"Internet": {
|
||||||
"account_number": "4922",
|
"account_number": "4922",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"B\u00fcrobedarf": {
|
"Bürobedarf": {
|
||||||
"account_number": "4930",
|
"account_number": "4930",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Zeitschriften, B\u00fccher": {
|
"Zeitschriften, Bücher": {
|
||||||
"account_number": "4940",
|
"account_number": "4940",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Fortbildungskosten": {
|
"Fortbildungskosten": {
|
||||||
"account_number": "4945",
|
"account_number": "4945",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Buchf\u00fchrungskosten": {
|
"Buchführungskosten": {
|
||||||
"account_number": "4955",
|
"account_number": "4955",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Abschlu\u00df- u. Pr\u00fcfungskosten": {
|
"Abschluß- u. Prüfungskosten": {
|
||||||
"account_number": "4957",
|
"account_number": "4957",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Nebenkosten des Geldverkehrs": {
|
"Nebenkosten des Geldverkehrs": {
|
||||||
"account_number": "4970",
|
"account_number": "4970",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Werkzeuge und Kleinger\u00e4te": {
|
"Werkzeuge und Kleingeräte": {
|
||||||
"account_number": "4985",
|
"account_number": "4985",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Zinsaufwendungen": {
|
"Zinsaufwendungen": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Zinsaufwendungen f\u00fcr kurzfristige Verbindlichkeiten": {
|
"Zinsaufwendungen für kurzfristige Verbindlichkeiten": {
|
||||||
"account_number": "2110",
|
"account_number": "2110",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
},
|
},
|
||||||
"Zinsaufwendungen f\u00fcr KFZ Finanzierung": {
|
"Zinsaufwendungen für KFZ Finanzierung": {
|
||||||
"account_number": "2121",
|
"account_number": "2121",
|
||||||
"account_type": "Expense Account"
|
"account_type": "Expense Account"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Anfangsbestand 9": {
|
"Anfangsbestand 9": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"root_type": "Equity",
|
"root_type": "Equity",
|
||||||
"Saldenvortragskonten": {
|
"Saldenvortragskonten": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Saldenvortrag Sachkonten": {
|
"Saldenvortrag Sachkonten": {
|
||||||
"account_number": "9000"
|
"account_number": "9000"
|
||||||
},
|
},
|
||||||
"Saldenvortr\u00e4ge Debitoren": {
|
"Saldenvorträge Debitoren": {
|
||||||
"account_number": "9008"
|
"account_number": "9008"
|
||||||
},
|
},
|
||||||
"Saldenvortr\u00e4ge Kreditoren": {
|
"Saldenvorträge Kreditoren": {
|
||||||
"account_number": "9009"
|
"account_number": "9009"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Privatkonten 1": {
|
"Privatkonten 1": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"root_type": "Equity",
|
"root_type": "Equity",
|
||||||
"Privatentnahmen/-einlagen": {
|
"Privatentnahmen/-einlagen": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Privatentnahme allgemein": {
|
"Privatentnahme allgemein": {
|
||||||
"account_number": "1800"
|
"account_number": "1800"
|
||||||
},
|
},
|
||||||
"Privatsteuern": {
|
"Privatsteuern": {
|
||||||
"account_number": "1810"
|
"account_number": "1810"
|
||||||
},
|
},
|
||||||
"Sonderausgaben beschr\u00e4nkt abzugsf\u00e4hig": {
|
"Sonderausgaben beschränkt abzugsfähig": {
|
||||||
"account_number": "1820"
|
"account_number": "1820"
|
||||||
},
|
},
|
||||||
"Sonderausgaben unbeschr\u00e4nkt abzugsf\u00e4hig": {
|
"Sonderausgaben unbeschränkt abzugsfähig": {
|
||||||
"account_number": "1830"
|
"account_number": "1830"
|
||||||
},
|
},
|
||||||
"Au\u00dfergew\u00f6hnliche Belastungen": {
|
"Außergewöhnliche Belastungen": {
|
||||||
"account_number": "1850"
|
"account_number": "1850"
|
||||||
},
|
},
|
||||||
"Privateinlagen": {
|
"Privateinlagen": {
|
||||||
"account_number": "1890"
|
"account_number": "1890"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Accounting Dimension Filter', {
|
frappe.ui.form.on('Accounting Dimension Filter', {
|
||||||
refresh: function(frm, cdt, cdn) {
|
refresh: function(frm, cdt, cdn) {
|
||||||
if (frm.doc.accounting_dimension) {
|
|
||||||
frm.set_df_property('dimensions', 'label', frm.doc.accounting_dimension, cdn, 'dimension_value');
|
|
||||||
}
|
|
||||||
|
|
||||||
let help_content =
|
let help_content =
|
||||||
`<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
|
`<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
|
||||||
<tr><td>
|
<tr><td>
|
||||||
@@ -68,6 +64,7 @@ frappe.ui.form.on('Accounting Dimension Filter', {
|
|||||||
frm.clear_table("dimensions");
|
frm.clear_table("dimensions");
|
||||||
let row = frm.add_child("dimensions");
|
let row = frm.add_child("dimensions");
|
||||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||||
|
frm.fields_dict["dimensions"].grid.update_docfield_property("dimension_value", "label", frm.doc.accounting_dimension);
|
||||||
frm.refresh_field("dimensions");
|
frm.refresh_field("dimensions");
|
||||||
frm.trigger('setup_filters');
|
frm.trigger('setup_filters');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -49,15 +49,9 @@ class AccountingPeriod(Document):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_doctypes_for_closing(self):
|
def get_doctypes_for_closing(self):
|
||||||
docs_for_closing = []
|
docs_for_closing = []
|
||||||
doctypes = [
|
# get period closing doctypes from all the apps
|
||||||
"Sales Invoice",
|
doctypes = frappe.get_hooks("period_closing_doctypes")
|
||||||
"Purchase Invoice",
|
|
||||||
"Journal Entry",
|
|
||||||
"Payroll Entry",
|
|
||||||
"Bank Clearance",
|
|
||||||
"Asset",
|
|
||||||
"Stock Entry",
|
|
||||||
]
|
|
||||||
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
||||||
for closed_doctype in closed_doctypes:
|
for closed_doctype in closed_doctypes:
|
||||||
docs_for_closing.append(closed_doctype)
|
docs_for_closing.append(closed_doctype)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"automatically_fetch_payment_terms",
|
"automatically_fetch_payment_terms",
|
||||||
"column_break_17",
|
"column_break_17",
|
||||||
"enable_common_party_accounting",
|
"enable_common_party_accounting",
|
||||||
|
"allow_multi_currency_invoices_against_single_party_account",
|
||||||
"report_setting_section",
|
"report_setting_section",
|
||||||
"use_custom_cash_flow",
|
"use_custom_cash_flow",
|
||||||
"deferred_accounting_settings_section",
|
"deferred_accounting_settings_section",
|
||||||
@@ -55,7 +56,9 @@
|
|||||||
"acc_frozen_upto",
|
"acc_frozen_upto",
|
||||||
"column_break_25",
|
"column_break_25",
|
||||||
"frozen_accounts_modifier",
|
"frozen_accounts_modifier",
|
||||||
"report_settings_sb"
|
"report_settings_sb",
|
||||||
|
"tab_break_dpet",
|
||||||
|
"show_balance_in_coa"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -90,7 +93,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"description": "Enabling ensure each Sales Invoice has a unique value in Supplier Invoice No. field",
|
"description": "Enabling ensure each Purchase Invoice has a unique value in Supplier Invoice No. field",
|
||||||
"fieldname": "check_supplier_invoice_uniqueness",
|
"fieldname": "check_supplier_invoice_uniqueness",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Check Supplier Invoice Number Uniqueness"
|
"label": "Check Supplier Invoice Number Uniqueness"
|
||||||
@@ -339,6 +342,24 @@
|
|||||||
"fieldname": "report_setting_section",
|
"fieldname": "report_setting_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Report Setting"
|
"label": "Report Setting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency",
|
||||||
|
"fieldname": "allow_multi_currency_invoices_against_single_party_account",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Allow multi-currency invoices against single party account "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tab_break_dpet",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Chart Of Accounts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "show_balance_in_coa",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Show Balances in Chart Of Accounts"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
@@ -346,7 +367,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-04-08 14:45:06.796418",
|
"modified": "2023-01-02 12:07:42.434214",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
|||||||
@@ -77,6 +77,6 @@ def get_party_bank_account(party_type, party):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_bank_account_details(bank_account):
|
def get_bank_account_details(bank_account):
|
||||||
return frappe.db.get_value(
|
return frappe.get_cached_value(
|
||||||
"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
|
"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,23 @@
|
|||||||
frappe.ui.form.on("Bank Clearance", {
|
frappe.ui.form.on("Bank Clearance", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("account", "account_currency", "account_currency");
|
frm.add_fetch("account", "account_currency", "account_currency");
|
||||||
|
|
||||||
|
frm.set_query("account", function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"account_type": ["in",["Bank","Cash"]],
|
||||||
|
"is_group": 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.set_query("bank_account", function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'is_company_account': 1
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
@@ -12,14 +29,7 @@ frappe.ui.form.on("Bank Clearance", {
|
|||||||
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
||||||
frm.set_value("account", default_bank_account);
|
frm.set_value("account", default_bank_account);
|
||||||
|
|
||||||
frm.set_query("account", function() {
|
|
||||||
return {
|
|
||||||
"filters": {
|
|
||||||
"account_type": ["in",["Bank","Cash"]],
|
|
||||||
"is_group": 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_value("from_date", frappe.datetime.month_start());
|
frm.set_value("from_date", frappe.datetime.month_start());
|
||||||
frm.set_value("to_date", frappe.datetime.month_end());
|
frm.set_value("to_date", frappe.datetime.month_end());
|
||||||
@@ -27,6 +37,11 @@ frappe.ui.form.on("Bank Clearance", {
|
|||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.disable_save();
|
frm.disable_save();
|
||||||
|
frm.add_custom_button(__('Get Payment Entries'), () =>
|
||||||
|
frm.trigger("get_payment_entries")
|
||||||
|
);
|
||||||
|
|
||||||
|
frm.change_custom_button_type('Get Payment Entries', null, 'primary');
|
||||||
},
|
},
|
||||||
|
|
||||||
update_clearance_date: function(frm) {
|
update_clearance_date: function(frm) {
|
||||||
@@ -36,22 +51,30 @@ frappe.ui.form.on("Bank Clearance", {
|
|||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
frm.refresh_field("payment_entries");
|
frm.refresh_field("payment_entries");
|
||||||
frm.refresh_fields();
|
frm.refresh_fields();
|
||||||
|
|
||||||
|
if (!frm.doc.payment_entries.length) {
|
||||||
|
frm.change_custom_button_type('Get Payment Entries', null, 'primary');
|
||||||
|
frm.change_custom_button_type('Update Clearance Date', null, 'default');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
get_payment_entries: function(frm) {
|
get_payment_entries: function(frm) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "get_payment_entries",
|
method: "get_payment_entries",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
frm.refresh_field("payment_entries");
|
frm.refresh_field("payment_entries");
|
||||||
frm.refresh_fields();
|
|
||||||
|
|
||||||
$(frm.fields_dict.payment_entries.wrapper).find("[data-fieldname=amount]").each(function(i,v){
|
if (frm.doc.payment_entries.length) {
|
||||||
if (i !=0){
|
frm.add_custom_button(__('Update Clearance Date'), () =>
|
||||||
$(v).addClass("text-right")
|
frm.trigger("update_clearance_date")
|
||||||
}
|
);
|
||||||
})
|
|
||||||
|
frm.change_custom_button_type('Get Payment Entries', null, 'default');
|
||||||
|
frm.change_custom_button_type('Update Clearance Date', null, 'primary');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_copy": 1,
|
"allow_copy": 1,
|
||||||
"creation": "2013-01-10 16:34:05",
|
"creation": "2013-01-10 16:34:05",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -13,11 +14,8 @@
|
|||||||
"bank_account",
|
"bank_account",
|
||||||
"include_reconciled_entries",
|
"include_reconciled_entries",
|
||||||
"include_pos_transactions",
|
"include_pos_transactions",
|
||||||
"get_payment_entries",
|
|
||||||
"section_break_10",
|
"section_break_10",
|
||||||
"payment_entries",
|
"payment_entries"
|
||||||
"update_clearance_date",
|
|
||||||
"total_amount"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -76,11 +74,6 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Include POS Transactions"
|
"label": "Include POS Transactions"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "get_payment_entries",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"label": "Get Payment Entries"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_10",
|
"fieldname": "section_break_10",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
@@ -91,25 +84,14 @@
|
|||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Payment Entries",
|
"label": "Payment Entries",
|
||||||
"options": "Bank Clearance Detail"
|
"options": "Bank Clearance Detail"
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "update_clearance_date",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"label": "Update Clearance Date"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "total_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"label": "Total Amount",
|
|
||||||
"options": "account_currency",
|
|
||||||
"read_only": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"icon": "fa fa-check",
|
"icon": "fa fa-check",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"modified": "2020-04-06 16:12:06.628008",
|
"links": [],
|
||||||
|
"modified": "2022-11-28 17:24:13.008692",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Clearance",
|
"name": "Bank Clearance",
|
||||||
@@ -126,5 +108,6 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC"
|
"sort_order": "ASC",
|
||||||
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -99,12 +99,12 @@ class BankClearance(Document):
|
|||||||
.where(loan_disbursement.clearance_date.isnull())
|
.where(loan_disbursement.clearance_date.isnull())
|
||||||
.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
|
.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
|
||||||
.orderby(loan_disbursement.disbursement_date)
|
.orderby(loan_disbursement.disbursement_date)
|
||||||
.orderby(loan_disbursement.name, frappe.qb.desc)
|
.orderby(loan_disbursement.name, order=frappe.qb.desc)
|
||||||
).run(as_dict=1)
|
).run(as_dict=1)
|
||||||
|
|
||||||
loan_repayment = frappe.qb.DocType("Loan Repayment")
|
loan_repayment = frappe.qb.DocType("Loan Repayment")
|
||||||
|
|
||||||
loan_repayments = (
|
query = (
|
||||||
frappe.qb.from_(loan_repayment)
|
frappe.qb.from_(loan_repayment)
|
||||||
.select(
|
.select(
|
||||||
ConstantColumn("Loan Repayment").as_("payment_document"),
|
ConstantColumn("Loan Repayment").as_("payment_document"),
|
||||||
@@ -118,13 +118,19 @@ class BankClearance(Document):
|
|||||||
)
|
)
|
||||||
.where(loan_repayment.docstatus == 1)
|
.where(loan_repayment.docstatus == 1)
|
||||||
.where(loan_repayment.clearance_date.isnull())
|
.where(loan_repayment.clearance_date.isnull())
|
||||||
.where(loan_repayment.repay_from_salary == 0)
|
|
||||||
.where(loan_repayment.posting_date >= self.from_date)
|
.where(loan_repayment.posting_date >= self.from_date)
|
||||||
.where(loan_repayment.posting_date <= self.to_date)
|
.where(loan_repayment.posting_date <= self.to_date)
|
||||||
.where(loan_repayment.payment_account.isin([self.bank_account, self.account]))
|
.where(loan_repayment.payment_account.isin([self.bank_account, self.account]))
|
||||||
.orderby(loan_repayment.posting_date)
|
)
|
||||||
.orderby(loan_repayment.name, frappe.qb.desc)
|
|
||||||
).run(as_dict=1)
|
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||||
|
query = query.where((loan_repayment.repay_from_salary == 0))
|
||||||
|
|
||||||
|
query = query.orderby(loan_repayment.posting_date).orderby(
|
||||||
|
loan_repayment.name, order=frappe.qb.desc
|
||||||
|
)
|
||||||
|
|
||||||
|
loan_repayments = query.run(as_dict=True)
|
||||||
|
|
||||||
pos_sales_invoices, pos_purchase_invoices = [], []
|
pos_sales_invoices, pos_purchase_invoices = [], []
|
||||||
if self.include_pos_transactions:
|
if self.include_pos_transactions:
|
||||||
@@ -173,7 +179,6 @@ class BankClearance(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.set("payment_entries", [])
|
self.set("payment_entries", [])
|
||||||
self.total_amount = 0.0
|
|
||||||
default_currency = erpnext.get_default_currency()
|
default_currency = erpnext.get_default_currency()
|
||||||
|
|
||||||
for d in entries:
|
for d in entries:
|
||||||
@@ -192,7 +197,6 @@ class BankClearance(Document):
|
|||||||
d.pop("debit")
|
d.pop("debit")
|
||||||
d.pop("account_currency")
|
d.pop("account_currency")
|
||||||
row.update(d)
|
row.update(d)
|
||||||
self.total_amount += flt(amount)
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_clearance_date(self):
|
def update_clearance_date(self):
|
||||||
|
|||||||
@@ -43,20 +43,13 @@ frappe.ui.form.on('Bank Guarantee', {
|
|||||||
|
|
||||||
reference_docname: function(frm) {
|
reference_docname: function(frm) {
|
||||||
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
||||||
let fields_to_fetch = ["grand_total"];
|
|
||||||
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
||||||
|
|
||||||
if (frm.doc.reference_doctype == "Sales Order") {
|
|
||||||
fields_to_fetch.push("project");
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_to_fetch.push(party_field);
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials",
|
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details",
|
||||||
args: {
|
args: {
|
||||||
"column_list": fields_to_fetch,
|
"bank_guarantee_type": frm.doc.bg_type,
|
||||||
"doctype": frm.doc.reference_doctype,
|
"reference_name": frm.doc.reference_docname
|
||||||
"docname": frm.doc.reference_docname
|
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
|
|||||||
@@ -2,11 +2,8 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.desk.search import sanitize_searchfield
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
@@ -25,14 +22,18 @@ class BankGuarantee(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_vouchar_detials(column_list, doctype, docname):
|
def get_voucher_details(bank_guarantee_type: str, reference_name: str):
|
||||||
column_list = json.loads(column_list)
|
if not isinstance(reference_name, str):
|
||||||
for col in column_list:
|
raise TypeError("reference_name must be a string")
|
||||||
sanitize_searchfield(col)
|
|
||||||
return frappe.db.sql(
|
fields_to_fetch = ["grand_total"]
|
||||||
""" select {columns} from `tab{doctype}` where name=%s""".format(
|
|
||||||
columns=", ".join(column_list), doctype=doctype
|
if bank_guarantee_type == "Receiving":
|
||||||
),
|
doctype = "Sales Order"
|
||||||
docname,
|
fields_to_fetch.append("customer")
|
||||||
as_dict=1,
|
fields_to_fetch.append("project")
|
||||||
)[0]
|
else:
|
||||||
|
doctype = "Purchase Order"
|
||||||
|
fields_to_fetch.append("supplier")
|
||||||
|
|
||||||
|
return frappe.db.get_value(doctype, reference_name, fields_to_fetch, as_dict=True)
|
||||||
|
|||||||
@@ -12,19 +12,31 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
let no_bank_transactions_text =
|
||||||
|
`<div class="text-muted text-center">${__("No Matching Bank Transactions Found")}</div>`
|
||||||
|
set_field_options("no_bank_transactions", no_bank_transactions_text);
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
frm.trigger('bank_account');
|
frm.trigger('bank_account');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
filter_by_reference_date: function (frm) {
|
||||||
|
if (frm.doc.filter_by_reference_date) {
|
||||||
|
frm.set_value("bank_statement_from_date", "");
|
||||||
|
frm.set_value("bank_statement_to_date", "");
|
||||||
|
} else {
|
||||||
|
frm.set_value("from_reference_date", "");
|
||||||
|
frm.set_value("to_reference_date", "");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
frappe.require("bank-reconciliation-tool.bundle.js", () =>
|
frappe.require("bank-reconciliation-tool.bundle.js", () =>
|
||||||
frm.trigger("make_reconciliation_tool")
|
frm.trigger("make_reconciliation_tool")
|
||||||
);
|
);
|
||||||
frm.upload_statement_button = frm.page.set_secondary_action(
|
|
||||||
__("Upload Bank Statement"),
|
frm.add_custom_button(__("Upload Bank Statement"), () =>
|
||||||
() =>
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:
|
method:
|
||||||
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
||||||
@@ -46,6 +58,20 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
frm.add_custom_button(__('Auto Reconcile'), function() {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
|
||||||
|
args: {
|
||||||
|
bank_account: frm.doc.bank_account,
|
||||||
|
from_date: frm.doc.bank_statement_from_date,
|
||||||
|
to_date: frm.doc.bank_statement_to_date,
|
||||||
|
filter_by_reference_date: frm.doc.filter_by_reference_date,
|
||||||
|
from_reference_date: frm.doc.from_reference_date,
|
||||||
|
to_reference_date: frm.doc.to_reference_date,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
after_save: function (frm) {
|
after_save: function (frm) {
|
||||||
@@ -157,6 +183,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
).$wrapper,
|
).$wrapper,
|
||||||
bank_statement_from_date: frm.doc.bank_statement_from_date,
|
bank_statement_from_date: frm.doc.bank_statement_from_date,
|
||||||
bank_statement_to_date: frm.doc.bank_statement_to_date,
|
bank_statement_to_date: frm.doc.bank_statement_to_date,
|
||||||
|
filter_by_reference_date: frm.doc.filter_by_reference_date,
|
||||||
|
from_reference_date: frm.doc.from_reference_date,
|
||||||
|
to_reference_date: frm.doc.to_reference_date,
|
||||||
bank_statement_closing_balance:
|
bank_statement_closing_balance:
|
||||||
frm.doc.bank_statement_closing_balance,
|
frm.doc.bank_statement_closing_balance,
|
||||||
cards_manager: frm.cards_manager,
|
cards_manager: frm.cards_manager,
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
"column_break_1",
|
"column_break_1",
|
||||||
"bank_statement_from_date",
|
"bank_statement_from_date",
|
||||||
"bank_statement_to_date",
|
"bank_statement_to_date",
|
||||||
|
"from_reference_date",
|
||||||
|
"to_reference_date",
|
||||||
|
"filter_by_reference_date",
|
||||||
"column_break_2",
|
"column_break_2",
|
||||||
"account_opening_balance",
|
"account_opening_balance",
|
||||||
"bank_statement_closing_balance",
|
"bank_statement_closing_balance",
|
||||||
@@ -36,13 +39,13 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.bank_account",
|
"depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
|
||||||
"fieldname": "bank_statement_from_date",
|
"fieldname": "bank_statement_from_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "From Date"
|
"label": "From Date"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.bank_statement_from_date",
|
"depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
|
||||||
"fieldname": "bank_statement_to_date",
|
"fieldname": "bank_statement_to_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "To Date"
|
"label": "To Date"
|
||||||
@@ -83,13 +86,31 @@
|
|||||||
"fieldname": "no_bank_transactions",
|
"fieldname": "no_bank_transactions",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
|
"options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.filter_by_reference_date",
|
||||||
|
"fieldname": "from_reference_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "From Reference Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.filter_by_reference_date",
|
||||||
|
"fieldname": "to_reference_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "To Reference Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "filter_by_reference_date",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Filter by Reference Date"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-21 11:13:49.831769",
|
"modified": "2023-01-13 13:00:02.022919",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Reconciliation Tool",
|
"name": "Bank Reconciliation Tool",
|
||||||
@@ -108,5 +129,6 @@
|
|||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,8 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.query_builder.custom import ConstantColumn
|
from frappe.query_builder.custom import ConstantColumn
|
||||||
from frappe.utils import flt
|
from frappe.utils import cint, flt
|
||||||
|
|
||||||
from erpnext import get_company_currency
|
|
||||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
|
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
|
||||||
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
||||||
get_amounts_not_reflected_in_system,
|
get_amounts_not_reflected_in_system,
|
||||||
@@ -51,6 +50,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
|||||||
"party",
|
"party",
|
||||||
],
|
],
|
||||||
filters=filters,
|
filters=filters,
|
||||||
|
order_by="date",
|
||||||
)
|
)
|
||||||
return transactions
|
return transactions
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_balance(bank_account, till_date):
|
def get_account_balance(bank_account, till_date):
|
||||||
# returns account balance till the specified date
|
# returns account balance till the specified date
|
||||||
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
account = frappe.get_cached_value("Bank Account", bank_account, "account")
|
||||||
filters = frappe._dict(
|
filters = frappe._dict(
|
||||||
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
|
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
|
||||||
)
|
)
|
||||||
@@ -131,8 +131,10 @@ def create_journal_entry_bts(
|
|||||||
fieldname=["name", "deposit", "withdrawal", "bank_account"],
|
fieldname=["name", "deposit", "withdrawal", "bank_account"],
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)[0]
|
)[0]
|
||||||
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
|
company_account = frappe.get_cached_value(
|
||||||
account_type = frappe.db.get_value("Account", second_account, "account_type")
|
"Bank Account", bank_transaction.bank_account, "account"
|
||||||
|
)
|
||||||
|
account_type = frappe.get_cached_value("Account", second_account, "account_type")
|
||||||
if account_type in ["Receivable", "Payable"]:
|
if account_type in ["Receivable", "Payable"]:
|
||||||
if not (party_type and party):
|
if not (party_type and party):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
@@ -165,7 +167,7 @@ def create_journal_entry_bts(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
company = frappe.get_value("Account", company_account, "company")
|
company = frappe.get_cached_value("Account", company_account, "company")
|
||||||
|
|
||||||
journal_entry_dict = {
|
journal_entry_dict = {
|
||||||
"voucher_type": entry_type,
|
"voucher_type": entry_type,
|
||||||
@@ -220,8 +222,10 @@ def create_payment_entry_bts(
|
|||||||
paid_amount = bank_transaction.unallocated_amount
|
paid_amount = bank_transaction.unallocated_amount
|
||||||
payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
|
payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
|
||||||
|
|
||||||
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
|
company_account = frappe.get_cached_value(
|
||||||
company = frappe.get_value("Account", company_account, "company")
|
"Bank Account", bank_transaction.bank_account, "account"
|
||||||
|
)
|
||||||
|
company = frappe.get_cached_value("Account", company_account, "company")
|
||||||
payment_entry_dict = {
|
payment_entry_dict = {
|
||||||
"company": company,
|
"company": company,
|
||||||
"payment_type": payment_type,
|
"payment_type": payment_type,
|
||||||
@@ -262,12 +266,86 @@ def create_payment_entry_bts(
|
|||||||
return reconcile_vouchers(bank_transaction.name, vouchers)
|
return reconcile_vouchers(bank_transaction.name, vouchers)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def auto_reconcile_vouchers(
|
||||||
|
bank_account,
|
||||||
|
from_date=None,
|
||||||
|
to_date=None,
|
||||||
|
filter_by_reference_date=None,
|
||||||
|
from_reference_date=None,
|
||||||
|
to_reference_date=None,
|
||||||
|
):
|
||||||
|
frappe.flags.auto_reconcile_vouchers = True
|
||||||
|
document_types = ["payment_entry", "journal_entry"]
|
||||||
|
bank_transactions = get_bank_transactions(bank_account)
|
||||||
|
matched_transaction = []
|
||||||
|
for transaction in bank_transactions:
|
||||||
|
linked_payments = get_linked_payments(
|
||||||
|
transaction.name,
|
||||||
|
document_types,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
)
|
||||||
|
vouchers = []
|
||||||
|
for r in linked_payments:
|
||||||
|
vouchers.append(
|
||||||
|
{
|
||||||
|
"payment_doctype": r[1],
|
||||||
|
"payment_name": r[2],
|
||||||
|
"amount": r[4],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
transaction = frappe.get_doc("Bank Transaction", transaction.name)
|
||||||
|
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||||
|
matched_trans = 0
|
||||||
|
for voucher in vouchers:
|
||||||
|
gl_entry = frappe.db.get_value(
|
||||||
|
"GL Entry",
|
||||||
|
dict(
|
||||||
|
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
|
||||||
|
),
|
||||||
|
["credit", "debit"],
|
||||||
|
as_dict=1,
|
||||||
|
)
|
||||||
|
gl_amount, transaction_amount = (
|
||||||
|
(gl_entry.credit, transaction.deposit)
|
||||||
|
if gl_entry.credit > 0
|
||||||
|
else (gl_entry.debit, transaction.withdrawal)
|
||||||
|
)
|
||||||
|
allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
|
||||||
|
transaction.append(
|
||||||
|
"payment_entries",
|
||||||
|
{
|
||||||
|
"payment_document": voucher["payment_doctype"],
|
||||||
|
"payment_entry": voucher["payment_name"],
|
||||||
|
"allocated_amount": allocated_amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
matched_transaction.append(str(transaction.name))
|
||||||
|
transaction.save()
|
||||||
|
transaction.update_allocations()
|
||||||
|
matched_transaction_len = len(set(matched_transaction))
|
||||||
|
if matched_transaction_len == 0:
|
||||||
|
frappe.msgprint(_("No matching references found for auto reconciliation"))
|
||||||
|
elif matched_transaction_len == 1:
|
||||||
|
frappe.msgprint(_("{0} transaction is reconcilied").format(matched_transaction_len))
|
||||||
|
else:
|
||||||
|
frappe.msgprint(_("{0} transactions are reconcilied").format(matched_transaction_len))
|
||||||
|
|
||||||
|
frappe.flags.auto_reconcile_vouchers = False
|
||||||
|
|
||||||
|
return frappe.get_doc("Bank Transaction", transaction.name)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def reconcile_vouchers(bank_transaction_name, vouchers):
|
def reconcile_vouchers(bank_transaction_name, vouchers):
|
||||||
# updated clear date of all the vouchers based on the bank transaction
|
# updated clear date of all the vouchers based on the bank transaction
|
||||||
vouchers = json.loads(vouchers)
|
vouchers = json.loads(vouchers)
|
||||||
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
||||||
company_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
company_account = frappe.get_cached_value("Bank Account", transaction.bank_account, "account")
|
||||||
|
|
||||||
if transaction.unallocated_amount == 0:
|
if transaction.unallocated_amount == 0:
|
||||||
frappe.throw(_("This bank transaction is already fully reconciled"))
|
frappe.throw(_("This bank transaction is already fully reconciled"))
|
||||||
@@ -291,7 +369,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
|
|||||||
"The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction"
|
"The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
account = frappe.get_cached_value("Bank Account", transaction.bank_account, "account")
|
||||||
|
|
||||||
for voucher in vouchers:
|
for voucher in vouchers:
|
||||||
gl_entry = frappe.db.get_value(
|
gl_entry = frappe.db.get_value(
|
||||||
@@ -299,7 +377,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
|
|||||||
dict(
|
dict(
|
||||||
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
|
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
|
||||||
),
|
),
|
||||||
["credit", "debit"],
|
["credit_in_account_currency as credit", "debit_in_account_currency as debit"],
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
gl_amount, transaction_amount = (
|
gl_amount, transaction_amount = (
|
||||||
@@ -324,20 +402,58 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_linked_payments(bank_transaction_name, document_types=None):
|
def get_linked_payments(
|
||||||
|
bank_transaction_name,
|
||||||
|
document_types=None,
|
||||||
|
from_date=None,
|
||||||
|
to_date=None,
|
||||||
|
filter_by_reference_date=None,
|
||||||
|
from_reference_date=None,
|
||||||
|
to_reference_date=None,
|
||||||
|
):
|
||||||
# get all matching payments for a bank transaction
|
# get all matching payments for a bank transaction
|
||||||
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
||||||
bank_account = frappe.db.get_values(
|
bank_account = frappe.db.get_values(
|
||||||
"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
|
"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
|
||||||
)[0]
|
)[0]
|
||||||
(account, company) = (bank_account.account, bank_account.company)
|
(account, company) = (bank_account.account, bank_account.company)
|
||||||
matching = check_matching(account, company, transaction, document_types)
|
matching = check_matching(
|
||||||
|
account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
)
|
||||||
return matching
|
return matching
|
||||||
|
|
||||||
|
|
||||||
def check_matching(bank_account, company, transaction, document_types):
|
def check_matching(
|
||||||
|
bank_account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
):
|
||||||
# combine all types of vouchers
|
# combine all types of vouchers
|
||||||
subquery = get_queries(bank_account, company, transaction, document_types)
|
subquery = get_queries(
|
||||||
|
bank_account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
)
|
||||||
filters = {
|
filters = {
|
||||||
"amount": transaction.unallocated_amount,
|
"amount": transaction.unallocated_amount,
|
||||||
"payment_type": "Receive" if transaction.deposit > 0 else "Pay",
|
"payment_type": "Receive" if transaction.deposit > 0 else "Pay",
|
||||||
@@ -358,22 +474,84 @@ def check_matching(bank_account, company, transaction, document_types):
|
|||||||
filters,
|
filters,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
|
return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
|
||||||
|
|
||||||
|
|
||||||
def get_queries(bank_account, company, transaction, document_types):
|
def get_queries(
|
||||||
|
bank_account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
):
|
||||||
# get queries to get matching vouchers
|
# get queries to get matching vouchers
|
||||||
amount_condition = "=" if "exact_match" in document_types else "<="
|
amount_condition = "=" if "exact_match" in document_types else "<="
|
||||||
account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
|
account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
|
||||||
queries = []
|
queries = []
|
||||||
|
|
||||||
|
# get matching queries from all the apps
|
||||||
|
for method_name in frappe.get_hooks("get_matching_queries"):
|
||||||
|
queries.extend(
|
||||||
|
frappe.get_attr(method_name)(
|
||||||
|
bank_account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
amount_condition,
|
||||||
|
account_from_to,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
)
|
||||||
|
or []
|
||||||
|
)
|
||||||
|
|
||||||
|
return queries
|
||||||
|
|
||||||
|
|
||||||
|
def get_matching_queries(
|
||||||
|
bank_account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
amount_condition,
|
||||||
|
account_from_to,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
):
|
||||||
|
queries = []
|
||||||
if "payment_entry" in document_types:
|
if "payment_entry" in document_types:
|
||||||
pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction)
|
pe_amount_matching = get_pe_matching_query(
|
||||||
|
amount_condition,
|
||||||
|
account_from_to,
|
||||||
|
transaction,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
)
|
||||||
queries.extend([pe_amount_matching])
|
queries.extend([pe_amount_matching])
|
||||||
|
|
||||||
if "journal_entry" in document_types:
|
if "journal_entry" in document_types:
|
||||||
je_amount_matching = get_je_matching_query(amount_condition, transaction)
|
je_amount_matching = get_je_matching_query(
|
||||||
|
amount_condition,
|
||||||
|
transaction,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
)
|
||||||
queries.extend([je_amount_matching])
|
queries.extend([je_amount_matching])
|
||||||
|
|
||||||
if transaction.deposit > 0 and "sales_invoice" in document_types:
|
if transaction.deposit > 0 and "sales_invoice" in document_types:
|
||||||
@@ -385,10 +563,6 @@ def get_queries(bank_account, company, transaction, document_types):
|
|||||||
pi_amount_matching = get_pi_matching_query(amount_condition)
|
pi_amount_matching = get_pi_matching_query(amount_condition)
|
||||||
queries.extend([pi_amount_matching])
|
queries.extend([pi_amount_matching])
|
||||||
|
|
||||||
if "expense_claim" in document_types:
|
|
||||||
ec_amount_matching = get_ec_matching_query(bank_account, company, amount_condition)
|
|
||||||
queries.extend([ec_amount_matching])
|
|
||||||
|
|
||||||
return queries
|
return queries
|
||||||
|
|
||||||
|
|
||||||
@@ -467,11 +641,13 @@ def get_lr_matching_query(bank_account, amount_condition, filters):
|
|||||||
loan_repayment.posting_date,
|
loan_repayment.posting_date,
|
||||||
)
|
)
|
||||||
.where(loan_repayment.docstatus == 1)
|
.where(loan_repayment.docstatus == 1)
|
||||||
.where(loan_repayment.repay_from_salary == 0)
|
|
||||||
.where(loan_repayment.clearance_date.isnull())
|
.where(loan_repayment.clearance_date.isnull())
|
||||||
.where(loan_repayment.payment_account == bank_account)
|
.where(loan_repayment.payment_account == bank_account)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||||
|
query = query.where((loan_repayment.repay_from_salary == 0))
|
||||||
|
|
||||||
if amount_condition:
|
if amount_condition:
|
||||||
query.where(loan_repayment.amount_paid == filters.get("amount"))
|
query.where(loan_repayment.amount_paid == filters.get("amount"))
|
||||||
else:
|
else:
|
||||||
@@ -482,47 +658,81 @@ def get_lr_matching_query(bank_account, amount_condition, filters):
|
|||||||
return vouchers
|
return vouchers
|
||||||
|
|
||||||
|
|
||||||
def get_pe_matching_query(amount_condition, account_from_to, transaction):
|
def get_pe_matching_query(
|
||||||
|
amount_condition,
|
||||||
|
account_from_to,
|
||||||
|
transaction,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
):
|
||||||
# get matching payment entries query
|
# get matching payment entries query
|
||||||
if transaction.deposit > 0:
|
if transaction.deposit > 0:
|
||||||
currency_field = "paid_to_account_currency as currency"
|
currency_field = "paid_to_account_currency as currency"
|
||||||
else:
|
else:
|
||||||
currency_field = "paid_from_account_currency as currency"
|
currency_field = "paid_from_account_currency as currency"
|
||||||
|
filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'"
|
||||||
|
order_by = " posting_date"
|
||||||
|
filter_by_reference_no = ""
|
||||||
|
if cint(filter_by_reference_date):
|
||||||
|
filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
|
||||||
|
order_by = " reference_date"
|
||||||
|
if frappe.flags.auto_reconcile_vouchers == True:
|
||||||
|
filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
|
||||||
return f"""
|
return f"""
|
||||||
SELECT
|
SELECT
|
||||||
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
|
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
|
||||||
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
|
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
|
||||||
+ 1 ) AS rank,
|
+ 1 ) AS rank,
|
||||||
'Payment Entry' as doctype,
|
'Payment Entry' as doctype,
|
||||||
name,
|
name,
|
||||||
paid_amount,
|
paid_amount,
|
||||||
reference_no,
|
reference_no,
|
||||||
reference_date,
|
reference_date,
|
||||||
party,
|
party,
|
||||||
party_type,
|
party_type,
|
||||||
posting_date,
|
posting_date,
|
||||||
{currency_field}
|
{currency_field}
|
||||||
FROM
|
FROM
|
||||||
`tabPayment Entry`
|
`tabPayment Entry`
|
||||||
WHERE
|
WHERE
|
||||||
paid_amount {amount_condition} %(amount)s
|
paid_amount {amount_condition} %(amount)s
|
||||||
AND docstatus = 1
|
AND docstatus = 1
|
||||||
AND payment_type IN (%(payment_type)s, 'Internal Transfer')
|
AND payment_type IN (%(payment_type)s, 'Internal Transfer')
|
||||||
AND ifnull(clearance_date, '') = ""
|
AND ifnull(clearance_date, '') = ""
|
||||||
AND {account_from_to} = %(bank_account)s
|
AND {account_from_to} = %(bank_account)s
|
||||||
|
{filter_by_date}
|
||||||
|
{filter_by_reference_no}
|
||||||
|
order by{order_by}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_je_matching_query(amount_condition, transaction):
|
def get_je_matching_query(
|
||||||
|
amount_condition,
|
||||||
|
transaction,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
):
|
||||||
# get matching journal entry query
|
# get matching journal entry query
|
||||||
|
|
||||||
# We have mapping at the bank level
|
# We have mapping at the bank level
|
||||||
# So one bank could have both types of bank accounts like asset and liability
|
# So one bank could have both types of bank accounts like asset and liability
|
||||||
# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
|
# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
|
||||||
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
|
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
|
||||||
|
filter_by_date = f"AND je.posting_date between '{from_date}' and '{to_date}'"
|
||||||
|
order_by = " je.posting_date"
|
||||||
|
filter_by_reference_no = ""
|
||||||
|
if cint(filter_by_reference_date):
|
||||||
|
filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
|
||||||
|
order_by = " je.cheque_date"
|
||||||
|
if frappe.flags.auto_reconcile_vouchers == True:
|
||||||
|
filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'"
|
||||||
return f"""
|
return f"""
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
|
(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
|
||||||
+ 1) AS rank ,
|
+ 1) AS rank ,
|
||||||
@@ -546,6 +756,9 @@ def get_je_matching_query(amount_condition, transaction):
|
|||||||
AND jea.account = %(bank_account)s
|
AND jea.account = %(bank_account)s
|
||||||
AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
|
AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
|
||||||
AND je.docstatus = 1
|
AND je.docstatus = 1
|
||||||
|
{filter_by_date}
|
||||||
|
{filter_by_reference_no}
|
||||||
|
order by {order_by}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -602,37 +815,3 @@ def get_pi_matching_query(amount_condition):
|
|||||||
AND ifnull(clearance_date, '') = ""
|
AND ifnull(clearance_date, '') = ""
|
||||||
AND cash_bank_account = %(bank_account)s
|
AND cash_bank_account = %(bank_account)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_ec_matching_query(bank_account, company, amount_condition):
|
|
||||||
# get matching Expense Claim query
|
|
||||||
mode_of_payments = [
|
|
||||||
x["parent"]
|
|
||||||
for x in frappe.db.get_all(
|
|
||||||
"Mode of Payment Account", filters={"default_account": bank_account}, fields=["parent"]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
mode_of_payments = "('" + "', '".join(mode_of_payments) + "' )"
|
|
||||||
company_currency = get_company_currency(company)
|
|
||||||
return f"""
|
|
||||||
SELECT
|
|
||||||
( CASE WHEN employee = %(party)s THEN 1 ELSE 0 END
|
|
||||||
+ 1 ) AS rank,
|
|
||||||
'Expense Claim' as doctype,
|
|
||||||
name,
|
|
||||||
total_sanctioned_amount as paid_amount,
|
|
||||||
'' as reference_no,
|
|
||||||
'' as reference_date,
|
|
||||||
employee as party,
|
|
||||||
'Employee' as party_type,
|
|
||||||
posting_date,
|
|
||||||
'{company_currency}' as currency
|
|
||||||
FROM
|
|
||||||
`tabExpense Claim`
|
|
||||||
WHERE
|
|
||||||
total_sanctioned_amount {amount_condition} %(amount)s
|
|
||||||
AND docstatus = 1
|
|
||||||
AND is_paid = 1
|
|
||||||
AND ifnull(clearance_date, '') = ""
|
|
||||||
AND mode_of_payment in {mode_of_payments}
|
|
||||||
"""
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
|
|
||||||
if (frm.doc.status.includes("Success")) {
|
if (frm.doc.status.includes("Success")) {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
__("Go to {0} List", [frm.doc.reference_doctype]),
|
__("Go to {0} List", [__(frm.doc.reference_doctype)]),
|
||||||
() => frappe.set_route("List", frm.doc.reference_doctype)
|
() => frappe.set_route("List", frm.doc.reference_doctype)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
show_import_status(frm) {
|
show_import_status(frm) {
|
||||||
let import_log = JSON.parse(frm.doc.import_log || "[]");
|
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||||
let successful_records = import_log.filter((log) => log.success);
|
let successful_records = import_log.filter((log) => log.success);
|
||||||
let failed_records = import_log.filter((log) => !log.success);
|
let failed_records = import_log.filter((log) => !log.success);
|
||||||
if (successful_records.length === 0) return;
|
if (successful_records.length === 0) return;
|
||||||
@@ -309,7 +309,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
|
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
|
||||||
|
|
||||||
show_import_preview(frm, preview_data) {
|
show_import_preview(frm, preview_data) {
|
||||||
let import_log = JSON.parse(frm.doc.import_log || "[]");
|
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||||
|
|
||||||
if (
|
if (
|
||||||
frm.import_preview &&
|
frm.import_preview &&
|
||||||
@@ -439,7 +439,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
show_import_log(frm) {
|
show_import_log(frm) {
|
||||||
let import_log = JSON.parse(frm.doc.import_log || "[]");
|
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||||
let logs = import_log;
|
let logs = import_log;
|
||||||
frm.toggle_display("import_log", false);
|
frm.toggle_display("import_log", false);
|
||||||
frm.toggle_display("import_log_section", logs.length > 0);
|
frm.toggle_display("import_log_section", logs.length > 0);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"section_import_preview",
|
"section_import_preview",
|
||||||
"import_preview",
|
"import_preview",
|
||||||
"import_log_section",
|
"import_log_section",
|
||||||
"import_log",
|
"statement_import_log",
|
||||||
"show_failed_logs",
|
"show_failed_logs",
|
||||||
"import_log_preview",
|
"import_log_preview",
|
||||||
"reference_doctype",
|
"reference_doctype",
|
||||||
@@ -90,12 +90,6 @@
|
|||||||
"options": "JSON",
|
"options": "JSON",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "import_log",
|
|
||||||
"fieldtype": "Code",
|
|
||||||
"label": "Import Log",
|
|
||||||
"options": "JSON"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "import_log_section",
|
"fieldname": "import_log_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@@ -198,11 +192,17 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "statement_import_log",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Statement Import Log",
|
||||||
|
"options": "JSON"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-05-12 14:17:37.777246",
|
"modified": "2022-09-07 11:11:40.293317",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Statement Import",
|
"name": "Bank Statement Import",
|
||||||
|
|||||||
@@ -53,15 +53,13 @@ class BankStatementImport(DataImport):
|
|||||||
if "Bank Account" not in json.dumps(preview["columns"]):
|
if "Bank Account" not in json.dumps(preview["columns"]):
|
||||||
frappe.throw(_("Please add the Bank Account column"))
|
frappe.throw(_("Please add the Bank Account column"))
|
||||||
|
|
||||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
from frappe.utils.background_jobs import is_job_queued
|
||||||
from frappe.utils.scheduler import is_scheduler_inactive
|
from frappe.utils.scheduler import is_scheduler_inactive
|
||||||
|
|
||||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
if is_scheduler_inactive() and not frappe.flags.in_test:
|
||||||
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
||||||
|
|
||||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
if not is_job_queued(self.name):
|
||||||
|
|
||||||
if self.name not in enqueued_jobs:
|
|
||||||
enqueue(
|
enqueue(
|
||||||
start_import,
|
start_import,
|
||||||
queue="default",
|
queue="default",
|
||||||
|
|||||||
@@ -3,28 +3,21 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("Bank Transaction", {
|
frappe.ui.form.on("Bank Transaction", {
|
||||||
onload(frm) {
|
onload(frm) {
|
||||||
frm.set_query("payment_document", "payment_entries", function () {
|
frm.set_query("payment_document", "payment_entries", function() {
|
||||||
|
const payment_doctypes = frm.events.get_payment_doctypes(frm);
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
name: [
|
name: ["in", payment_doctypes],
|
||||||
"in",
|
|
||||||
[
|
|
||||||
"Payment Entry",
|
|
||||||
"Journal Entry",
|
|
||||||
"Sales Invoice",
|
|
||||||
"Purchase Invoice",
|
|
||||||
"Expense Claim",
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
bank_account: function (frm) {
|
|
||||||
|
bank_account: function(frm) {
|
||||||
set_bank_statement_filter(frm);
|
set_bank_statement_filter(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: function (frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("party_type", function () {
|
frm.set_query("party_type", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
@@ -33,6 +26,16 @@ frappe.ui.form.on("Bank Transaction", {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get_payment_doctypes: function() {
|
||||||
|
// get payment doctypes from all the apps
|
||||||
|
return [
|
||||||
|
"Payment Entry",
|
||||||
|
"Journal Entry",
|
||||||
|
"Sales Invoice",
|
||||||
|
"Purchase Invoice",
|
||||||
|
];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Bank Transaction Payments", {
|
frappe.ui.form.on("Bank Transaction Payments", {
|
||||||
|
|||||||
@@ -58,18 +58,10 @@ class BankTransaction(StatusUpdater):
|
|||||||
|
|
||||||
def clear_linked_payment_entries(self, for_cancel=False):
|
def clear_linked_payment_entries(self, for_cancel=False):
|
||||||
for payment_entry in self.payment_entries:
|
for payment_entry in self.payment_entries:
|
||||||
if payment_entry.payment_document in [
|
if payment_entry.payment_document == "Sales Invoice":
|
||||||
"Payment Entry",
|
|
||||||
"Journal Entry",
|
|
||||||
"Purchase Invoice",
|
|
||||||
"Expense Claim",
|
|
||||||
"Loan Repayment",
|
|
||||||
"Loan Disbursement",
|
|
||||||
]:
|
|
||||||
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
|
|
||||||
|
|
||||||
elif payment_entry.payment_document == "Sales Invoice":
|
|
||||||
self.clear_sales_invoice(payment_entry, for_cancel=for_cancel)
|
self.clear_sales_invoice(payment_entry, for_cancel=for_cancel)
|
||||||
|
elif payment_entry.payment_document in get_doctypes_for_bank_reconciliation():
|
||||||
|
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
|
||||||
|
|
||||||
def clear_simple_entry(self, payment_entry, for_cancel=False):
|
def clear_simple_entry(self, payment_entry, for_cancel=False):
|
||||||
if payment_entry.payment_document == "Payment Entry":
|
if payment_entry.payment_document == "Payment Entry":
|
||||||
@@ -95,6 +87,12 @@ class BankTransaction(StatusUpdater):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_doctypes_for_bank_reconciliation():
|
||||||
|
"""Get Bank Reconciliation doctypes from all the apps"""
|
||||||
|
return frappe.get_hooks("bank_reconciliation_doctypes")
|
||||||
|
|
||||||
|
|
||||||
def get_reconciled_bank_transactions(payment_entry):
|
def get_reconciled_bank_transactions(payment_entry):
|
||||||
reconciled_bank_transactions = frappe.get_all(
|
reconciled_bank_transactions = frappe.get_all(
|
||||||
"Bank Transaction Payments",
|
"Bank Transaction Payments",
|
||||||
@@ -139,7 +137,7 @@ def get_paid_amount(payment_entry, currency, bank_account):
|
|||||||
)
|
)
|
||||||
elif doc.payment_type == "Pay":
|
elif doc.payment_type == "Pay":
|
||||||
paid_amount_field = (
|
paid_amount_field = (
|
||||||
"paid_amount" if doc.paid_to_account_currency == currency else "base_paid_amount"
|
"paid_amount" if doc.paid_from_account_currency == currency else "base_paid_amount"
|
||||||
)
|
)
|
||||||
|
|
||||||
return frappe.db.get_value(
|
return frappe.db.get_value(
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ def get_header_mapping(columns, bank_account):
|
|||||||
|
|
||||||
|
|
||||||
def get_bank_mapping(bank_account):
|
def get_bank_mapping(bank_account):
|
||||||
bank_name = frappe.db.get_value("Bank Account", bank_account, "bank")
|
bank_name = frappe.get_cached_value("Bank Account", bank_account, "bank")
|
||||||
bank = frappe.get_doc("Bank", bank_name)
|
bank = frappe.get_doc("Bank", bank_name)
|
||||||
|
|
||||||
mapping = {row.file_field: row.bank_transaction_field for row in bank.bank_transaction_mapping}
|
mapping = {row.file_field: row.bank_transaction_field for row in bank.bank_transaction_mapping}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import json
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import utils
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||||
get_linked_payments,
|
get_linked_payments,
|
||||||
@@ -18,35 +20,33 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
|
|||||||
test_dependencies = ["Item", "Cost Center"]
|
test_dependencies = ["Item", "Cost Center"]
|
||||||
|
|
||||||
|
|
||||||
class TestBankTransaction(unittest.TestCase):
|
class TestBankTransaction(FrappeTestCase):
|
||||||
@classmethod
|
def setUp(self):
|
||||||
def setUpClass(cls):
|
for dt in [
|
||||||
|
"Loan Repayment",
|
||||||
|
"Bank Transaction",
|
||||||
|
"Payment Entry",
|
||||||
|
"Payment Entry Reference",
|
||||||
|
"POS Profile",
|
||||||
|
]:
|
||||||
|
frappe.db.delete(dt)
|
||||||
|
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
add_transactions()
|
add_transactions()
|
||||||
add_vouchers()
|
add_vouchers()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
for bt in frappe.get_all("Bank Transaction"):
|
|
||||||
doc = frappe.get_doc("Bank Transaction", bt.name)
|
|
||||||
if doc.docstatus == 1:
|
|
||||||
doc.cancel()
|
|
||||||
doc.delete()
|
|
||||||
|
|
||||||
# Delete directly in DB to avoid validation errors for countries not allowing deletion
|
|
||||||
frappe.db.sql("""delete from `tabPayment Entry Reference`""")
|
|
||||||
frappe.db.sql("""delete from `tabPayment Entry`""")
|
|
||||||
|
|
||||||
# Delete POS Profile
|
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
|
||||||
|
|
||||||
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
||||||
def test_linked_payments(self):
|
def test_linked_payments(self):
|
||||||
bank_transaction = frappe.get_doc(
|
bank_transaction = frappe.get_doc(
|
||||||
"Bank Transaction",
|
"Bank Transaction",
|
||||||
dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
|
dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
|
||||||
)
|
)
|
||||||
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
|
linked_payments = get_linked_payments(
|
||||||
|
bank_transaction.name,
|
||||||
|
["payment_entry", "exact_match"],
|
||||||
|
from_date=bank_transaction.date,
|
||||||
|
to_date=utils.today(),
|
||||||
|
)
|
||||||
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
|
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
|
||||||
|
|
||||||
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
||||||
@@ -87,7 +87,12 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
"Bank Transaction",
|
"Bank Transaction",
|
||||||
dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
|
dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
|
||||||
)
|
)
|
||||||
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
|
linked_payments = get_linked_payments(
|
||||||
|
bank_transaction.name,
|
||||||
|
["payment_entry", "exact_match"],
|
||||||
|
from_date=bank_transaction.date,
|
||||||
|
to_date=utils.today(),
|
||||||
|
)
|
||||||
self.assertTrue(linked_payments[0][3])
|
self.assertTrue(linked_payments[0][3])
|
||||||
|
|
||||||
# Check error if already reconciled
|
# Check error if already reconciled
|
||||||
@@ -155,6 +160,35 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_matching_loan_repayment(self):
|
||||||
|
from erpnext.loan_management.doctype.loan.test_loan import create_loan_accounts
|
||||||
|
|
||||||
|
create_loan_accounts()
|
||||||
|
bank_account = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Bank Account",
|
||||||
|
"account_name": "Payment Account",
|
||||||
|
"bank": "Citi Bank",
|
||||||
|
"account": "Payment Account - _TC",
|
||||||
|
}
|
||||||
|
).insert(ignore_if_duplicate=True)
|
||||||
|
|
||||||
|
bank_transaction = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Bank Transaction",
|
||||||
|
"description": "Loan Repayment - OPSKATTUZWXXX AT776000000098709837 Herr G",
|
||||||
|
"date": "2018-10-27",
|
||||||
|
"deposit": 500,
|
||||||
|
"currency": "INR",
|
||||||
|
"bank_account": bank_account.name,
|
||||||
|
}
|
||||||
|
).submit()
|
||||||
|
|
||||||
|
repayment_entry = create_loan_and_repayment()
|
||||||
|
|
||||||
|
linked_payments = get_linked_payments(bank_transaction.name, ["loan_repayment", "exact_match"])
|
||||||
|
self.assertEqual(linked_payments[0][2], repayment_entry.name)
|
||||||
|
|
||||||
|
|
||||||
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
||||||
try:
|
try:
|
||||||
@@ -364,3 +398,59 @@ def add_vouchers():
|
|||||||
)
|
)
|
||||||
si.insert()
|
si.insert()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
|
|
||||||
|
def create_loan_and_repayment():
|
||||||
|
from erpnext.loan_management.doctype.loan.test_loan import (
|
||||||
|
create_loan,
|
||||||
|
create_loan_type,
|
||||||
|
create_repayment_entry,
|
||||||
|
make_loan_disbursement_entry,
|
||||||
|
)
|
||||||
|
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
|
||||||
|
process_loan_interest_accrual_for_term_loans,
|
||||||
|
)
|
||||||
|
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
|
create_loan_type(
|
||||||
|
"Personal Loan",
|
||||||
|
500000,
|
||||||
|
8.4,
|
||||||
|
is_term_loan=1,
|
||||||
|
mode_of_payment="Cash",
|
||||||
|
disbursement_account="Disbursement Account - _TC",
|
||||||
|
payment_account="Payment Account - _TC",
|
||||||
|
loan_account="Loan Account - _TC",
|
||||||
|
interest_income_account="Interest Income Account - _TC",
|
||||||
|
penalty_income_account="Penalty Income Account - _TC",
|
||||||
|
)
|
||||||
|
|
||||||
|
applicant = make_employee("test_bank_reco@loan.com", company="_Test Company")
|
||||||
|
loan = create_loan(applicant, "Personal Loan", 5000, "Repay Over Number of Periods", 20)
|
||||||
|
loan = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Loan",
|
||||||
|
"applicant_type": "Employee",
|
||||||
|
"company": "_Test Company",
|
||||||
|
"applicant": applicant,
|
||||||
|
"loan_type": "Personal Loan",
|
||||||
|
"loan_amount": 5000,
|
||||||
|
"repayment_method": "Repay Fixed Amount per Period",
|
||||||
|
"monthly_repayment_amount": 500,
|
||||||
|
"repayment_start_date": "2018-09-27",
|
||||||
|
"is_term_loan": 1,
|
||||||
|
"posting_date": "2018-09-27",
|
||||||
|
}
|
||||||
|
).insert()
|
||||||
|
|
||||||
|
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date="2018-09-27")
|
||||||
|
process_loan_interest_accrual_for_term_loans(posting_date="2018-10-27")
|
||||||
|
|
||||||
|
repayment_entry = create_repayment_entry(
|
||||||
|
loan.name,
|
||||||
|
applicant,
|
||||||
|
"2018-10-27",
|
||||||
|
500,
|
||||||
|
)
|
||||||
|
repayment_entry.submit()
|
||||||
|
return repayment_entry
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
|
"autoname": "naming_series:",
|
||||||
"creation": "2016-05-16 11:42:29.632528",
|
"creation": "2016-05-16 11:42:29.632528",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
"budget_against",
|
"budget_against",
|
||||||
"company",
|
"company",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
|
"naming_series",
|
||||||
"project",
|
"project",
|
||||||
"fiscal_year",
|
"fiscal_year",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
@@ -190,15 +192,26 @@
|
|||||||
"label": "Budget Accounts",
|
"label": "Budget Accounts",
|
||||||
"options": "Budget Account",
|
"options": "Budget Account",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "naming_series",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Series",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1,
|
||||||
|
"set_only_once": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-06 15:13:54.055854",
|
"modified": "2022-10-10 22:14:36.361509",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Budget",
|
"name": "Budget",
|
||||||
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@@ -220,5 +233,6 @@
|
|||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.naming import make_autoname
|
|
||||||
from frappe.utils import add_months, flt, fmt_money, get_last_day, getdate
|
from frappe.utils import add_months, flt, fmt_money, get_last_day, getdate
|
||||||
|
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||||
@@ -23,11 +22,6 @@ class DuplicateBudgetError(frappe.ValidationError):
|
|||||||
|
|
||||||
|
|
||||||
class Budget(Document):
|
class Budget(Document):
|
||||||
def autoname(self):
|
|
||||||
self.name = make_autoname(
|
|
||||||
self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###"
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if not self.get(frappe.scrub(self.budget_against)):
|
if not self.get(frappe.scrub(self.budget_against)):
|
||||||
frappe.throw(_("{0} is mandatory").format(self.budget_against))
|
frappe.throw(_("{0} is mandatory").format(self.budget_against))
|
||||||
@@ -65,7 +59,7 @@ class Budget(Document):
|
|||||||
account_list = []
|
account_list = []
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.account:
|
if d.account:
|
||||||
account_details = frappe.db.get_value(
|
account_details = frappe.get_cached_value(
|
||||||
"Account", d.account, ["is_group", "company", "report_type"], as_dict=1
|
"Account", d.account, ["is_group", "company", "report_type"], as_dict=1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,8 +103,11 @@ class Budget(Document):
|
|||||||
):
|
):
|
||||||
self.applicable_on_booking_actual_expenses = 1
|
self.applicable_on_booking_actual_expenses = 1
|
||||||
|
|
||||||
|
def before_naming(self):
|
||||||
|
self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###"
|
||||||
|
|
||||||
def validate_expense_against_budget(args):
|
|
||||||
|
def validate_expense_against_budget(args, expense_amount=0):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
if args.get("company") and not args.fiscal_year:
|
if args.get("company") and not args.fiscal_year:
|
||||||
@@ -178,15 +175,20 @@ def validate_expense_against_budget(args):
|
|||||||
) # nosec
|
) # nosec
|
||||||
|
|
||||||
if budget_records:
|
if budget_records:
|
||||||
validate_budget_records(args, budget_records)
|
validate_budget_records(args, budget_records, expense_amount)
|
||||||
|
|
||||||
|
|
||||||
def validate_budget_records(args, budget_records):
|
def validate_budget_records(args, budget_records, expense_amount):
|
||||||
for budget in budget_records:
|
for budget in budget_records:
|
||||||
if flt(budget.budget_amount):
|
if flt(budget.budget_amount):
|
||||||
amount = get_amount(args, budget)
|
amount = expense_amount or get_amount(args, budget)
|
||||||
yearly_action, monthly_action = get_actions(args, budget)
|
yearly_action, monthly_action = get_actions(args, budget)
|
||||||
|
|
||||||
|
if yearly_action in ("Stop", "Warn"):
|
||||||
|
compare_expense_with_budget(
|
||||||
|
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
|
||||||
|
)
|
||||||
|
|
||||||
if monthly_action in ["Stop", "Warn"]:
|
if monthly_action in ["Stop", "Warn"]:
|
||||||
budget_amount = get_accumulated_monthly_budget(
|
budget_amount = get_accumulated_monthly_budget(
|
||||||
budget.monthly_distribution, args.posting_date, args.fiscal_year, budget.budget_amount
|
budget.monthly_distribution, args.posting_date, args.fiscal_year, budget.budget_amount
|
||||||
@@ -198,28 +200,28 @@ def validate_budget_records(args, budget_records):
|
|||||||
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
|
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
|
||||||
yearly_action in ("Stop", "Warn")
|
|
||||||
and monthly_action != "Stop"
|
|
||||||
and yearly_action != monthly_action
|
|
||||||
):
|
|
||||||
compare_expense_with_budget(
|
|
||||||
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
||||||
actual_expense = amount or get_actual_expense(args)
|
actual_expense = get_actual_expense(args)
|
||||||
if actual_expense > budget_amount:
|
total_expense = actual_expense + amount
|
||||||
diff = actual_expense - budget_amount
|
|
||||||
|
if total_expense > budget_amount:
|
||||||
|
if actual_expense > budget_amount:
|
||||||
|
error_tense = _("is already")
|
||||||
|
diff = actual_expense - budget_amount
|
||||||
|
else:
|
||||||
|
error_tense = _("will be")
|
||||||
|
diff = total_expense - budget_amount
|
||||||
|
|
||||||
currency = frappe.get_cached_value("Company", args.company, "default_currency")
|
currency = frappe.get_cached_value("Company", args.company, "default_currency")
|
||||||
|
|
||||||
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
|
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It {5} exceed by {6}").format(
|
||||||
_(action_for),
|
_(action_for),
|
||||||
frappe.bold(args.account),
|
frappe.bold(args.account),
|
||||||
args.budget_against_field,
|
frappe.unscrub(args.budget_against_field),
|
||||||
frappe.bold(budget_against),
|
frappe.bold(budget_against),
|
||||||
frappe.bold(fmt_money(budget_amount, currency=currency)),
|
frappe.bold(fmt_money(budget_amount, currency=currency)),
|
||||||
|
error_tense,
|
||||||
frappe.bold(fmt_money(diff, currency=currency)),
|
frappe.bold(fmt_money(diff, currency=currency)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -230,9 +232,9 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
|||||||
action = "Warn"
|
action = "Warn"
|
||||||
|
|
||||||
if action == "Stop":
|
if action == "Stop":
|
||||||
frappe.throw(msg, BudgetError)
|
frappe.throw(msg, BudgetError, title=_("Budget Exceeded"))
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(msg, indicator="orange")
|
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
|
||||||
|
|
||||||
|
|
||||||
def get_actions(args, budget):
|
def get_actions(args, budget):
|
||||||
@@ -309,7 +311,7 @@ def get_other_condition(args, budget, for_doc):
|
|||||||
|
|
||||||
if args.get("fiscal_year"):
|
if args.get("fiscal_year"):
|
||||||
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
|
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
|
||||||
start_date, end_date = frappe.db.get_value(
|
start_date, end_date = frappe.get_cached_value(
|
||||||
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -354,7 +356,9 @@ def get_actual_expense(args):
|
|||||||
"""
|
"""
|
||||||
select sum(gle.debit) - sum(gle.credit)
|
select sum(gle.debit) - sum(gle.credit)
|
||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
where gle.account=%(account)s
|
where
|
||||||
|
is_cancelled = 0
|
||||||
|
and gle.account=%(account)s
|
||||||
{condition1}
|
{condition1}
|
||||||
and gle.fiscal_year=%(fiscal_year)s
|
and gle.fiscal_year=%(fiscal_year)s
|
||||||
and gle.company=%(company)s
|
and gle.company=%(company)s
|
||||||
@@ -382,7 +386,7 @@ def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_ye
|
|||||||
):
|
):
|
||||||
distribution.setdefault(d.month, d.percentage_allocation)
|
distribution.setdefault(d.month, d.percentage_allocation)
|
||||||
|
|
||||||
dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")
|
dt = frappe.get_cached_value("Fiscal Year", fiscal_year, "year_start_date")
|
||||||
accumulated_percentage = 0.0
|
accumulated_percentage = 0.0
|
||||||
|
|
||||||
while dt <= getdate(posting_date):
|
while dt <= getdate(posting_date):
|
||||||
|
|||||||
@@ -334,6 +334,39 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
jv.cancel()
|
jv.cancel()
|
||||||
|
|
||||||
|
def test_monthly_budget_against_main_cost_center(self):
|
||||||
|
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||||
|
from erpnext.accounts.doctype.cost_center_allocation.test_cost_center_allocation import (
|
||||||
|
create_cost_center_allocation,
|
||||||
|
)
|
||||||
|
|
||||||
|
cost_centers = [
|
||||||
|
"Main Budget Cost Center 1",
|
||||||
|
"Sub Budget Cost Center 1",
|
||||||
|
"Sub Budget Cost Center 2",
|
||||||
|
]
|
||||||
|
|
||||||
|
for cc in cost_centers:
|
||||||
|
create_cost_center(cost_center_name=cc, company="_Test Company")
|
||||||
|
|
||||||
|
create_cost_center_allocation(
|
||||||
|
"_Test Company",
|
||||||
|
"Main Budget Cost Center 1 - _TC",
|
||||||
|
{"Sub Budget Cost Center 1 - _TC": 60, "Sub Budget Cost Center 2 - _TC": 40},
|
||||||
|
)
|
||||||
|
|
||||||
|
make_budget(budget_against="Cost Center", cost_center="Main Budget Cost Center 1 - _TC")
|
||||||
|
|
||||||
|
jv = make_journal_entry(
|
||||||
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC",
|
||||||
|
400000,
|
||||||
|
"Main Budget Cost Center 1 - _TC",
|
||||||
|
posting_date=nowdate(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(BudgetError, jv.submit)
|
||||||
|
|
||||||
|
|
||||||
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
||||||
if budget_against_field == "project":
|
if budget_against_field == "project":
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
C Form (India specific only) - Will be deprecated.
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
||||||
// License: GNU General Public License v3. See license.txt
|
|
||||||
|
|
||||||
//c-form js file
|
|
||||||
// -----------------------------
|
|
||||||
|
|
||||||
frappe.ui.form.on('C-Form', {
|
|
||||||
setup(frm) {
|
|
||||||
frm.fields_dict.invoices.grid.get_field("invoice_no").get_query = function(doc) {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"docstatus": 1,
|
|
||||||
"customer": doc.customer,
|
|
||||||
"company": doc.company,
|
|
||||||
"c_form_applicable": 'Yes',
|
|
||||||
"c_form_no": ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
frm.fields_dict.state.get_query = function() {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
country: "India"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frappe.ui.form.on('C-Form Invoice Detail', {
|
|
||||||
invoice_no(frm, cdt, cdn) {
|
|
||||||
let d = frappe.get_doc(cdt, cdn);
|
|
||||||
|
|
||||||
if (d.invoice_no) {
|
|
||||||
frm.call('get_invoice_details', {
|
|
||||||
invoice_no: d.invoice_no
|
|
||||||
}).then(r => {
|
|
||||||
frappe.model.set_value(cdt, cdn, r.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,511 +0,0 @@
|
|||||||
{
|
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "naming_series:",
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-03-07 11:55:06",
|
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
|
||||||
"editable_grid": 0,
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "",
|
|
||||||
"fieldname": "naming_series",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Series",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "ACC-CF-.YYYY.-",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 1,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "c_form_no",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "C-Form No",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "received_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Received Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "customer",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Customer",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Customer",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break1",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "50%",
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "50%"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "company",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "quarter",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Quarter",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nI\nII\nIII\nIV",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "total_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Total Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "state",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "State",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break0",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "invoices",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Invoices",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "C-Form Invoice Detail",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "total_invoiced_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Total Invoiced Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "amended_from",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 1,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amended From",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "C-Form",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"icon": "fa fa-file-text",
|
|
||||||
"idx": 1,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 1,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 3,
|
|
||||||
"modified": "2018-08-21 14:44:30.558767",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "C-Form",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 1,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 1,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 1,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "All",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"timeline_field": "customer",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
||||||
# License: GNU General Public License v3. See license.txt
|
|
||||||
|
|
||||||
|
|
||||||
import frappe
|
|
||||||
from frappe import _
|
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe.utils import flt
|
|
||||||
|
|
||||||
|
|
||||||
class CForm(Document):
|
|
||||||
def validate(self):
|
|
||||||
"""Validate invoice that c-form is applicable
|
|
||||||
and no other c-form is received for that"""
|
|
||||||
|
|
||||||
for d in self.get("invoices"):
|
|
||||||
if d.invoice_no:
|
|
||||||
inv = frappe.db.sql(
|
|
||||||
"""select c_form_applicable, c_form_no from
|
|
||||||
`tabSales Invoice` where name = %s and docstatus = 1""",
|
|
||||||
d.invoice_no,
|
|
||||||
)
|
|
||||||
|
|
||||||
if inv and inv[0][0] != "Yes":
|
|
||||||
frappe.throw(_("C-form is not applicable for Invoice: {0}").format(d.invoice_no))
|
|
||||||
|
|
||||||
elif inv and inv[0][1] and inv[0][1] != self.name:
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"""Invoice {0} is tagged in another C-form: {1}.
|
|
||||||
If you want to change C-form no for this invoice,
|
|
||||||
please remove invoice no from the previous c-form and then try again""".format(
|
|
||||||
d.invoice_no, inv[0][1]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif not inv:
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"Row {0}: Invoice {1} is invalid, it might be cancelled / does not exist. \
|
|
||||||
Please enter a valid Invoice".format(
|
|
||||||
d.idx, d.invoice_no
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_update(self):
|
|
||||||
"""Update C-Form No on invoices"""
|
|
||||||
self.set_total_invoiced_amount()
|
|
||||||
|
|
||||||
def on_submit(self):
|
|
||||||
self.set_cform_in_sales_invoices()
|
|
||||||
|
|
||||||
def before_cancel(self):
|
|
||||||
# remove cform reference
|
|
||||||
frappe.db.sql("""update `tabSales Invoice` set c_form_no=null where c_form_no=%s""", self.name)
|
|
||||||
|
|
||||||
def set_cform_in_sales_invoices(self):
|
|
||||||
inv = [d.invoice_no for d in self.get("invoices")]
|
|
||||||
if inv:
|
|
||||||
frappe.db.sql(
|
|
||||||
"""update `tabSales Invoice` set c_form_no=%s, modified=%s where name in (%s)"""
|
|
||||||
% ("%s", "%s", ", ".join(["%s"] * len(inv))),
|
|
||||||
tuple([self.name, self.modified] + inv),
|
|
||||||
)
|
|
||||||
|
|
||||||
frappe.db.sql(
|
|
||||||
"""update `tabSales Invoice` set c_form_no = null, modified = %s
|
|
||||||
where name not in (%s) and ifnull(c_form_no, '') = %s"""
|
|
||||||
% ("%s", ", ".join(["%s"] * len(inv)), "%s"),
|
|
||||||
tuple([self.modified] + inv + [self.name]),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
frappe.throw(_("Please enter atleast 1 invoice in the table"))
|
|
||||||
|
|
||||||
def set_total_invoiced_amount(self):
|
|
||||||
total = sum(flt(d.grand_total) for d in self.get("invoices"))
|
|
||||||
frappe.db.set(self, "total_invoiced_amount", total)
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_invoice_details(self, invoice_no):
|
|
||||||
"""Pull details from invoices for referrence"""
|
|
||||||
if invoice_no:
|
|
||||||
inv = frappe.db.get_value(
|
|
||||||
"Sales Invoice",
|
|
||||||
invoice_no,
|
|
||||||
["posting_date", "territory", "base_net_total", "base_grand_total"],
|
|
||||||
as_dict=True,
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"invoice_date": inv.posting_date,
|
|
||||||
"territory": inv.territory,
|
|
||||||
"net_total": inv.base_net_total,
|
|
||||||
"grand_total": inv.base_grand_total,
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
||||||
# See license.txt
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
# test_records = frappe.get_test_records('C-Form')
|
|
||||||
|
|
||||||
|
|
||||||
class TestCForm(unittest.TestCase):
|
|
||||||
pass
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Invoice detail for parent C-Form.
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
{
|
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-02-22 01:27:38",
|
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "invoice_no",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Invoice No",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Sales Invoice",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "160px",
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "160px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "invoice_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Invoice Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "120px",
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "120px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"description": "",
|
|
||||||
"fieldname": "territory",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Territory",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Territory",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "120px",
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "120px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "net_total",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Net Total",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "120px",
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "120px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "grand_total",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Grand Total",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "120px",
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "120px"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 1,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2016-07-11 03:27:58.768719",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "C-Form Invoice Detail",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
||||||
# License: GNU General Public License v3. See license.txt
|
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
|
|
||||||
class CFormInvoiceDetail(Document):
|
|
||||||
pass
|
|
||||||
@@ -45,14 +45,14 @@ def validate_columns(data):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_company(company):
|
def validate_company(company):
|
||||||
parent_company, allow_account_creation_against_child_company = frappe.db.get_value(
|
parent_company, allow_account_creation_against_child_company = frappe.get_cached_value(
|
||||||
"Company", {"name": company}, ["parent_company", "allow_account_creation_against_child_company"]
|
"Company", company, ["parent_company", "allow_account_creation_against_child_company"]
|
||||||
)
|
)
|
||||||
|
|
||||||
if parent_company and (not allow_account_creation_against_child_company):
|
if parent_company and (not allow_account_creation_against_child_company):
|
||||||
msg = _("{} is a child company.").format(frappe.bold(company)) + " "
|
msg = _("{} is a child company.").format(frappe.bold(company)) + " "
|
||||||
msg += _("Please import accounts against parent company or enable {} in company master.").format(
|
msg += _("Please import accounts against parent company or enable {} in company master.").format(
|
||||||
frappe.bold("Allow Account Creation Against Child Company")
|
frappe.bold(_("Allow Account Creation Against Child Company"))
|
||||||
)
|
)
|
||||||
frappe.throw(msg, title=_("Wrong Company"))
|
frappe.throw(msg, title=_("Wrong Company"))
|
||||||
|
|
||||||
@@ -485,6 +485,10 @@ def set_default_accounts(company):
|
|||||||
"default_payable_account": frappe.db.get_value(
|
"default_payable_account": frappe.db.get_value(
|
||||||
"Account", {"company": company.name, "account_type": "Payable", "is_group": 0}
|
"Account", {"company": company.name, "account_type": "Payable", "is_group": 0}
|
||||||
),
|
),
|
||||||
|
"default_provisional_account": frappe.db.get_value(
|
||||||
|
"Account",
|
||||||
|
{"company": company.name, "account_type": "Service Received But Not Billed", "is_group": 0},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class CostCenter(NestedSet):
|
|||||||
from erpnext.accounts.utils import get_autoname_with_number
|
from erpnext.accounts.utils import get_autoname_with_number
|
||||||
|
|
||||||
self.name = get_autoname_with_number(
|
self.name = get_autoname_with_number(
|
||||||
self.cost_center_number, self.cost_center_name, None, self.company
|
self.cost_center_number, self.cost_center_name, self.company
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
|||||||
@@ -28,9 +28,14 @@ class InvalidDateError(frappe.ValidationError):
|
|||||||
|
|
||||||
|
|
||||||
class CostCenterAllocation(Document):
|
class CostCenterAllocation(Document):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CostCenterAllocation, self).__init__(*args, **kwargs)
|
||||||
|
self._skip_from_date_validation = False
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_total_allocation_percentage()
|
self.validate_total_allocation_percentage()
|
||||||
self.validate_from_date_based_on_existing_gle()
|
if not self._skip_from_date_validation:
|
||||||
|
self.validate_from_date_based_on_existing_gle()
|
||||||
self.validate_backdated_allocation()
|
self.validate_backdated_allocation()
|
||||||
self.validate_main_cost_center()
|
self.validate_main_cost_center()
|
||||||
self.validate_child_cost_centers()
|
self.validate_child_cost_centers()
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ def test_create_test_data():
|
|||||||
"item_name": "_Test Tesla Car",
|
"item_name": "_Test Tesla Car",
|
||||||
"apply_warehouse_wise_reorder_level": 0,
|
"apply_warehouse_wise_reorder_level": 0,
|
||||||
"warehouse": "Stores - _TC",
|
"warehouse": "Stores - _TC",
|
||||||
"gst_hsn_code": "999800",
|
|
||||||
"valuation_rate": 5000,
|
"valuation_rate": 5000,
|
||||||
"standard_rate": 5000,
|
"standard_rate": 5000,
|
||||||
"item_defaults": [
|
"item_defaults": [
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"api_details_section",
|
"api_details_section",
|
||||||
|
"disabled",
|
||||||
"service_provider",
|
"service_provider",
|
||||||
"api_endpoint",
|
"api_endpoint",
|
||||||
"url",
|
"url",
|
||||||
@@ -77,12 +78,18 @@
|
|||||||
"label": "Service Provider",
|
"label": "Service Provider",
|
||||||
"options": "frankfurter.app\nexchangerate.host\nCustom",
|
"options": "frankfurter.app\nexchangerate.host\nCustom",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Disabled"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-01-10 15:51:14.521174",
|
"modified": "2023-01-09 12:19:03.955906",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Currency Exchange Settings",
|
"name": "Currency Exchange Settings",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Dunning(AccountsController):
|
|||||||
self.validate_overdue_days()
|
self.validate_overdue_days()
|
||||||
self.validate_amount()
|
self.validate_amount()
|
||||||
if not self.income_account:
|
if not self.income_account:
|
||||||
self.income_account = frappe.db.get_value("Company", self.company, "default_income_account")
|
self.income_account = frappe.get_cached_value("Company", self.company, "default_income_account")
|
||||||
|
|
||||||
def validate_overdue_days(self):
|
def validate_overdue_days(self):
|
||||||
self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0
|
self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
frm.add_custom_button(__('Journal Entry'), function() {
|
frm.add_custom_button(__('Journal Entries'), function() {
|
||||||
return frm.events.make_jv(frm);
|
return frm.events.make_jv(frm);
|
||||||
}, __('Create'));
|
}, __('Create'));
|
||||||
}
|
}
|
||||||
@@ -35,10 +35,11 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get_entries: function(frm) {
|
get_entries: function(frm, account) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "get_accounts_data",
|
method: "get_accounts_data",
|
||||||
doc: cur_frm.doc,
|
doc: cur_frm.doc,
|
||||||
|
account: account,
|
||||||
callback: function(r){
|
callback: function(r){
|
||||||
frappe.model.clear_table(frm.doc, "accounts");
|
frappe.model.clear_table(frm.doc, "accounts");
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
@@ -57,7 +58,6 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
|
|
||||||
let total_gain_loss = 0;
|
let total_gain_loss = 0;
|
||||||
frm.doc.accounts.forEach((d) => {
|
frm.doc.accounts.forEach((d) => {
|
||||||
d.gain_loss = flt(d.new_balance_in_base_currency, precision("new_balance_in_base_currency", d)) - flt(d.balance_in_base_currency, precision("balance_in_base_currency", d));
|
|
||||||
total_gain_loss += flt(d.gain_loss, precision("gain_loss", d));
|
total_gain_loss += flt(d.gain_loss, precision("gain_loss", d));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,13 +66,19 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
make_jv : function(frm) {
|
make_jv : function(frm) {
|
||||||
|
let revaluation_journal = null;
|
||||||
|
let zero_balance_journal = null;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "make_jv_entry",
|
method: "make_jv_entries",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: "Making Journal Entries...",
|
||||||
callback: function(r){
|
callback: function(r){
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
var doc = frappe.model.sync(r.message)[0];
|
let response = r.message;
|
||||||
frappe.set_route("Form", doc.doctype, doc.name);
|
if(response['revaluation_jv'] || response['zero_balance_jv']) {
|
||||||
|
frappe.msgprint(__("Journals have been created"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,389 +1,160 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"autoname": "ACC-ERR-.YYYY.-.#####",
|
||||||
"allow_rename": 0,
|
"creation": "2018-04-13 18:25:55.943587",
|
||||||
"autoname": "ACC-ERR-.YYYY.-.#####",
|
"doctype": "DocType",
|
||||||
"beta": 0,
|
"editable_grid": 1,
|
||||||
"creation": "2018-04-13 18:25:55.943587",
|
"engine": "InnoDB",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"docstatus": 0,
|
"posting_date",
|
||||||
"doctype": "DocType",
|
"column_break_2",
|
||||||
"document_type": "",
|
"company",
|
||||||
"editable_grid": 1,
|
"section_break_4",
|
||||||
"engine": "InnoDB",
|
"get_entries",
|
||||||
|
"accounts",
|
||||||
|
"section_break_6",
|
||||||
|
"gain_loss_unbooked",
|
||||||
|
"gain_loss_booked",
|
||||||
|
"column_break_10",
|
||||||
|
"total_gain_loss",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "Today",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "posting_date",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Date",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Posting Date",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"default": "Today",
|
},
|
||||||
"fieldname": "posting_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Posting Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_2",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_2",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "company",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Company",
|
||||||
"collapsible": 0,
|
"options": "Company",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fieldname": "company",
|
},
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_4",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_4",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "get_entries",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Button",
|
||||||
"allow_on_submit": 0,
|
"label": "Get Entries"
|
||||||
"bold": 0,
|
},
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "get_entries",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Get Entries",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "accounts",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Exchange Rate Revaluation Account",
|
||||||
"bold": 0,
|
"no_copy": 1,
|
||||||
"collapsible": 0,
|
"options": "Exchange Rate Revaluation Account",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fieldname": "accounts",
|
},
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Exchange Rate Revaluation Account",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Exchange Rate Revaluation Account",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_6",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_6",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "amended_from",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"label": "Amended From",
|
||||||
"bold": 0,
|
"no_copy": 1,
|
||||||
"collapsible": 0,
|
"options": "Exchange Rate Revaluation",
|
||||||
"columns": 0,
|
"print_hide": 1,
|
||||||
"fieldname": "total_gain_loss",
|
"read_only": 1
|
||||||
"fieldtype": "Currency",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Total Gain/Loss",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "gain_loss_unbooked",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Currency",
|
||||||
"allow_on_submit": 0,
|
"label": "Gain/Loss from Revaluation",
|
||||||
"bold": 0,
|
"options": "Company:company:default_currency",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "amended_from",
|
{
|
||||||
"fieldtype": "Link",
|
"description": "Gain/Loss accumulated in foreign currency account. Accounts with '0' balance in either Base or Account currency",
|
||||||
"hidden": 0,
|
"fieldname": "gain_loss_booked",
|
||||||
"ignore_user_permissions": 0,
|
"fieldtype": "Currency",
|
||||||
"ignore_xss_filter": 0,
|
"label": "Gain/Loss already booked",
|
||||||
"in_filter": 0,
|
"options": "Company:company:default_currency",
|
||||||
"in_global_search": 0,
|
"read_only": 1
|
||||||
"in_list_view": 0,
|
},
|
||||||
"in_standard_filter": 0,
|
{
|
||||||
"label": "Amended From",
|
"fieldname": "total_gain_loss",
|
||||||
"length": 0,
|
"fieldtype": "Currency",
|
||||||
"no_copy": 1,
|
"label": "Total Gain/Loss",
|
||||||
"options": "Exchange Rate Revaluation",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"print_hide": 1,
|
},
|
||||||
"print_hide_if_no_value": 0,
|
{
|
||||||
"read_only": 1,
|
"fieldname": "column_break_10",
|
||||||
"remember_last_selected_value": 0,
|
"fieldtype": "Column Break"
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"is_submittable": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2022-12-29 19:38:24.416529",
|
||||||
"idx": 0,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Exchange Rate Revaluation",
|
||||||
"is_submittable": 1,
|
"naming_rule": "Expression (old style)",
|
||||||
"issingle": 0,
|
"owner": "Administrator",
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-08-21 16:15:34.660715",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Exchange Rate Revaluation",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "System Manager",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "System Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "Accounts Manager",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
"print": 1,
|
||||||
"import": 0,
|
"read": 1,
|
||||||
"permlevel": 0,
|
"report": 1,
|
||||||
"print": 1,
|
"role": "Accounts User",
|
||||||
"read": 1,
|
"share": 1,
|
||||||
"report": 1,
|
"submit": 1,
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 1,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC",
|
||||||
"read_only_onload": 0,
|
"states": [],
|
||||||
"show_name_in_global_search": 0,
|
"track_changes": 1
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,12 @@
|
|||||||
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _, qb
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
from frappe.utils import flt
|
from frappe.query_builder import Criterion, Order
|
||||||
|
from frappe.query_builder.functions import NullIf, Sum
|
||||||
|
from frappe.utils import flt, get_link_to_form
|
||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_balance_on
|
from erpnext.accounts.doctype.journal_entry.journal_entry import get_balance_on
|
||||||
@@ -19,11 +21,25 @@ class ExchangeRateRevaluation(Document):
|
|||||||
|
|
||||||
def set_total_gain_loss(self):
|
def set_total_gain_loss(self):
|
||||||
total_gain_loss = 0
|
total_gain_loss = 0
|
||||||
|
|
||||||
|
gain_loss_booked = 0
|
||||||
|
gain_loss_unbooked = 0
|
||||||
|
|
||||||
for d in self.accounts:
|
for d in self.accounts:
|
||||||
d.gain_loss = flt(
|
if not d.zero_balance:
|
||||||
d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")
|
d.gain_loss = flt(
|
||||||
) - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
|
d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")
|
||||||
|
) - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
|
||||||
|
|
||||||
|
if d.zero_balance:
|
||||||
|
gain_loss_booked += flt(d.gain_loss, d.precision("gain_loss"))
|
||||||
|
else:
|
||||||
|
gain_loss_unbooked += flt(d.gain_loss, d.precision("gain_loss"))
|
||||||
|
|
||||||
total_gain_loss += flt(d.gain_loss, d.precision("gain_loss"))
|
total_gain_loss += flt(d.gain_loss, d.precision("gain_loss"))
|
||||||
|
|
||||||
|
self.gain_loss_booked = gain_loss_booked
|
||||||
|
self.gain_loss_unbooked = gain_loss_unbooked
|
||||||
self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss"))
|
self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss"))
|
||||||
|
|
||||||
def validate_mandatory(self):
|
def validate_mandatory(self):
|
||||||
@@ -35,98 +51,206 @@ class ExchangeRateRevaluation(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def check_journal_entry_condition(self):
|
def check_journal_entry_condition(self):
|
||||||
total_debit = frappe.db.get_value(
|
exchange_gain_loss_account = self.get_for_unrealized_gain_loss_account()
|
||||||
"Journal Entry Account",
|
|
||||||
{"reference_type": "Exchange Rate Revaluation", "reference_name": self.name, "docstatus": 1},
|
jea = qb.DocType("Journal Entry Account")
|
||||||
"sum(debit) as sum",
|
journals = (
|
||||||
|
qb.from_(jea)
|
||||||
|
.select(jea.parent)
|
||||||
|
.distinct()
|
||||||
|
.where(
|
||||||
|
(jea.reference_type == "Exchange Rate Revaluation")
|
||||||
|
& (jea.reference_name == self.name)
|
||||||
|
& (jea.docstatus == 1)
|
||||||
|
)
|
||||||
|
.run()
|
||||||
)
|
)
|
||||||
|
|
||||||
total_amt = 0
|
if journals:
|
||||||
for d in self.accounts:
|
gle = qb.DocType("GL Entry")
|
||||||
total_amt = total_amt + d.new_balance_in_base_currency
|
total_amt = (
|
||||||
|
qb.from_(gle)
|
||||||
|
.select((Sum(gle.credit) - Sum(gle.debit)).as_("total_amount"))
|
||||||
|
.where(
|
||||||
|
(gle.voucher_type == "Journal Entry")
|
||||||
|
& (gle.voucher_no.isin(journals))
|
||||||
|
& (gle.account == exchange_gain_loss_account)
|
||||||
|
& (gle.is_cancelled == 0)
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
)
|
||||||
|
|
||||||
if total_amt != total_debit:
|
if total_amt and total_amt[0][0] != self.total_gain_loss:
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
return False
|
return True
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_accounts_data(self, account=None):
|
def get_accounts_data(self):
|
||||||
accounts = []
|
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
company_currency = erpnext.get_company_currency(self.company)
|
account_details = self.get_account_balance_from_gle(
|
||||||
|
company=self.company, posting_date=self.posting_date, account=None, party_type=None, party=None
|
||||||
|
)
|
||||||
|
accounts_with_new_balance = self.calculate_new_account_balance(
|
||||||
|
self.company, self.posting_date, account_details
|
||||||
|
)
|
||||||
|
|
||||||
|
if not accounts_with_new_balance:
|
||||||
|
self.throw_invalid_response_message(account_details)
|
||||||
|
|
||||||
|
return accounts_with_new_balance
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_account_balance_from_gle(company, posting_date, account, party_type, party):
|
||||||
|
account_details = []
|
||||||
|
|
||||||
|
if company and posting_date:
|
||||||
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
|
||||||
|
acc = qb.DocType("Account")
|
||||||
|
if account:
|
||||||
|
accounts = [account]
|
||||||
|
else:
|
||||||
|
res = (
|
||||||
|
qb.from_(acc)
|
||||||
|
.select(acc.name)
|
||||||
|
.where(
|
||||||
|
(acc.is_group == 0)
|
||||||
|
& (acc.report_type == "Balance Sheet")
|
||||||
|
& (acc.root_type.isin(["Asset", "Liability", "Equity"]))
|
||||||
|
& (acc.account_type != "Stock")
|
||||||
|
& (acc.company == company)
|
||||||
|
& (acc.account_currency != company_currency)
|
||||||
|
)
|
||||||
|
.orderby(acc.name)
|
||||||
|
.run(as_list=True)
|
||||||
|
)
|
||||||
|
accounts = [x[0] for x in res]
|
||||||
|
|
||||||
|
if accounts:
|
||||||
|
having_clause = (qb.Field("balance") != qb.Field("balance_in_account_currency")) & (
|
||||||
|
(qb.Field("balance_in_account_currency") != 0) | (qb.Field("balance") != 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
gle = qb.DocType("GL Entry")
|
||||||
|
|
||||||
|
# conditions
|
||||||
|
conditions = []
|
||||||
|
conditions.append(gle.account.isin(accounts))
|
||||||
|
conditions.append(gle.posting_date.lte(posting_date))
|
||||||
|
conditions.append(gle.is_cancelled == 0)
|
||||||
|
|
||||||
|
if party_type:
|
||||||
|
conditions.append(gle.party_type == party_type)
|
||||||
|
if party:
|
||||||
|
conditions.append(gle.party == party)
|
||||||
|
|
||||||
|
account_details = (
|
||||||
|
qb.from_(gle)
|
||||||
|
.select(
|
||||||
|
gle.account,
|
||||||
|
gle.party_type,
|
||||||
|
gle.party,
|
||||||
|
gle.account_currency,
|
||||||
|
(Sum(gle.debit_in_account_currency) - Sum(gle.credit_in_account_currency)).as_(
|
||||||
|
"balance_in_account_currency"
|
||||||
|
),
|
||||||
|
(Sum(gle.debit) - Sum(gle.credit)).as_("balance"),
|
||||||
|
(Sum(gle.debit) - Sum(gle.credit) == 0)
|
||||||
|
^ (Sum(gle.debit_in_account_currency) - Sum(gle.credit_in_account_currency) == 0).as_(
|
||||||
|
"zero_balance"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.where(Criterion.all(conditions))
|
||||||
|
.groupby(gle.account, NullIf(gle.party_type, ""), NullIf(gle.party, ""))
|
||||||
|
.having(having_clause)
|
||||||
|
.orderby(gle.account)
|
||||||
|
.run(as_dict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
return account_details
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def calculate_new_account_balance(company, posting_date, account_details):
|
||||||
|
accounts = []
|
||||||
|
company_currency = erpnext.get_company_currency(company)
|
||||||
precision = get_field_precision(
|
precision = get_field_precision(
|
||||||
frappe.get_meta("Exchange Rate Revaluation Account").get_field("new_balance_in_base_currency"),
|
frappe.get_meta("Exchange Rate Revaluation Account").get_field("new_balance_in_base_currency"),
|
||||||
company_currency,
|
company_currency,
|
||||||
)
|
)
|
||||||
|
|
||||||
account_details = self.get_accounts_from_gle()
|
if account_details:
|
||||||
for d in account_details:
|
# Handle Accounts with balance in both Account/Base Currency
|
||||||
current_exchange_rate = (
|
for d in [x for x in account_details if not x.zero_balance]:
|
||||||
d.balance / d.balance_in_account_currency if d.balance_in_account_currency else 0
|
current_exchange_rate = (
|
||||||
)
|
d.balance / d.balance_in_account_currency if d.balance_in_account_currency else 0
|
||||||
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, self.posting_date)
|
|
||||||
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
|
|
||||||
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
|
|
||||||
if gain_loss:
|
|
||||||
accounts.append(
|
|
||||||
{
|
|
||||||
"account": d.account,
|
|
||||||
"party_type": d.party_type,
|
|
||||||
"party": d.party,
|
|
||||||
"account_currency": d.account_currency,
|
|
||||||
"balance_in_base_currency": d.balance,
|
|
||||||
"balance_in_account_currency": d.balance_in_account_currency,
|
|
||||||
"current_exchange_rate": current_exchange_rate,
|
|
||||||
"new_exchange_rate": new_exchange_rate,
|
|
||||||
"new_balance_in_base_currency": new_balance_in_base_currency,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date)
|
||||||
|
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
|
||||||
|
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
|
||||||
|
if gain_loss:
|
||||||
|
accounts.append(
|
||||||
|
{
|
||||||
|
"account": d.account,
|
||||||
|
"party_type": d.party_type,
|
||||||
|
"party": d.party,
|
||||||
|
"account_currency": d.account_currency,
|
||||||
|
"balance_in_base_currency": d.balance,
|
||||||
|
"balance_in_account_currency": d.balance_in_account_currency,
|
||||||
|
"zero_balance": d.zero_balance,
|
||||||
|
"current_exchange_rate": current_exchange_rate,
|
||||||
|
"new_exchange_rate": new_exchange_rate,
|
||||||
|
"new_balance_in_base_currency": new_balance_in_base_currency,
|
||||||
|
"new_balance_in_account_currency": d.balance_in_account_currency,
|
||||||
|
"gain_loss": gain_loss,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if not accounts:
|
# Handle Accounts with '0' balance in Account/Base Currency
|
||||||
self.throw_invalid_response_message(account_details)
|
for d in [x for x in account_details if x.zero_balance]:
|
||||||
|
|
||||||
|
# TODO: Set new balance in Base/Account currency
|
||||||
|
if d.balance > 0:
|
||||||
|
current_exchange_rate = new_exchange_rate = 0
|
||||||
|
|
||||||
|
new_balance_in_account_currency = 0 # this will be '0'
|
||||||
|
new_balance_in_base_currency = 0 # this will be '0'
|
||||||
|
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
|
||||||
|
else:
|
||||||
|
new_exchange_rate = 0
|
||||||
|
new_balance_in_base_currency = 0
|
||||||
|
new_balance_in_account_currency = 0
|
||||||
|
|
||||||
|
current_exchange_rate = calculate_exchange_rate_using_last_gle(
|
||||||
|
company, d.account, d.party_type, d.party
|
||||||
|
)
|
||||||
|
|
||||||
|
gain_loss = new_balance_in_account_currency - (
|
||||||
|
current_exchange_rate * d.balance_in_account_currency
|
||||||
|
)
|
||||||
|
|
||||||
|
if gain_loss:
|
||||||
|
accounts.append(
|
||||||
|
{
|
||||||
|
"account": d.account,
|
||||||
|
"party_type": d.party_type,
|
||||||
|
"party": d.party,
|
||||||
|
"account_currency": d.account_currency,
|
||||||
|
"balance_in_base_currency": d.balance,
|
||||||
|
"balance_in_account_currency": d.balance_in_account_currency,
|
||||||
|
"zero_balance": d.zero_balance,
|
||||||
|
"current_exchange_rate": current_exchange_rate,
|
||||||
|
"new_exchange_rate": new_exchange_rate,
|
||||||
|
"new_balance_in_base_currency": new_balance_in_base_currency,
|
||||||
|
"new_balance_in_account_currency": new_balance_in_account_currency,
|
||||||
|
"gain_loss": gain_loss,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
def get_accounts_from_gle(self):
|
|
||||||
company_currency = erpnext.get_company_currency(self.company)
|
|
||||||
accounts = frappe.db.sql_list(
|
|
||||||
"""
|
|
||||||
select name
|
|
||||||
from tabAccount
|
|
||||||
where is_group = 0
|
|
||||||
and report_type = 'Balance Sheet'
|
|
||||||
and root_type in ('Asset', 'Liability', 'Equity')
|
|
||||||
and account_type != 'Stock'
|
|
||||||
and company=%s
|
|
||||||
and account_currency != %s
|
|
||||||
order by name""",
|
|
||||||
(self.company, company_currency),
|
|
||||||
)
|
|
||||||
|
|
||||||
account_details = []
|
|
||||||
if accounts:
|
|
||||||
account_details = frappe.db.sql(
|
|
||||||
"""
|
|
||||||
select
|
|
||||||
account, party_type, party, account_currency,
|
|
||||||
sum(debit_in_account_currency) - sum(credit_in_account_currency) as balance_in_account_currency,
|
|
||||||
sum(debit) - sum(credit) as balance
|
|
||||||
from `tabGL Entry`
|
|
||||||
where account in (%s)
|
|
||||||
and posting_date <= %s
|
|
||||||
and is_cancelled = 0
|
|
||||||
group by account, NULLIF(party_type,''), NULLIF(party,'')
|
|
||||||
having sum(debit) != sum(credit)
|
|
||||||
order by account
|
|
||||||
"""
|
|
||||||
% (", ".join(["%s"] * len(accounts)), "%s"),
|
|
||||||
tuple(accounts + [self.posting_date]),
|
|
||||||
as_dict=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
return account_details
|
|
||||||
|
|
||||||
def throw_invalid_response_message(self, account_details):
|
def throw_invalid_response_message(self, account_details):
|
||||||
if account_details:
|
if account_details:
|
||||||
message = _("No outstanding invoices require exchange rate revaluation")
|
message = _("No outstanding invoices require exchange rate revaluation")
|
||||||
@@ -134,11 +258,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
message = _("No outstanding invoices found")
|
message = _("No outstanding invoices found")
|
||||||
frappe.msgprint(message)
|
frappe.msgprint(message)
|
||||||
|
|
||||||
@frappe.whitelist()
|
def get_for_unrealized_gain_loss_account(self):
|
||||||
def make_jv_entry(self):
|
|
||||||
if self.total_gain_loss == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
unrealized_exchange_gain_loss_account = frappe.get_cached_value(
|
unrealized_exchange_gain_loss_account = frappe.get_cached_value(
|
||||||
"Company", self.company, "unrealized_exchange_gain_loss_account"
|
"Company", self.company, "unrealized_exchange_gain_loss_account"
|
||||||
)
|
)
|
||||||
@@ -146,6 +266,130 @@ class ExchangeRateRevaluation(Document):
|
|||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Please set Unrealized Exchange Gain/Loss Account in Company {0}").format(self.company)
|
_("Please set Unrealized Exchange Gain/Loss Account in Company {0}").format(self.company)
|
||||||
)
|
)
|
||||||
|
return unrealized_exchange_gain_loss_account
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_jv_entries(self):
|
||||||
|
zero_balance_jv = self.make_jv_for_zero_balance()
|
||||||
|
if zero_balance_jv:
|
||||||
|
frappe.msgprint(
|
||||||
|
f"Zero Balance Journal: {get_link_to_form('Journal Entry', zero_balance_jv.name)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
revaluation_jv = self.make_jv_for_revaluation()
|
||||||
|
if revaluation_jv:
|
||||||
|
frappe.msgprint(
|
||||||
|
f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
|
||||||
|
"zero_balance_jv": zero_balance_jv.name if zero_balance_jv else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
def make_jv_for_zero_balance(self):
|
||||||
|
if self.gain_loss_booked == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
accounts = [x for x in self.accounts if x.zero_balance]
|
||||||
|
|
||||||
|
if not accounts:
|
||||||
|
return
|
||||||
|
|
||||||
|
unrealized_exchange_gain_loss_account = self.get_for_unrealized_gain_loss_account()
|
||||||
|
|
||||||
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
|
journal_entry.voucher_type = "Exchange Gain Or Loss"
|
||||||
|
journal_entry.company = self.company
|
||||||
|
journal_entry.posting_date = self.posting_date
|
||||||
|
journal_entry.multi_currency = 1
|
||||||
|
|
||||||
|
journal_entry_accounts = []
|
||||||
|
for d in accounts:
|
||||||
|
journal_account = frappe._dict(
|
||||||
|
{
|
||||||
|
"account": d.get("account"),
|
||||||
|
"party_type": d.get("party_type"),
|
||||||
|
"party": d.get("party"),
|
||||||
|
"account_currency": d.get("account_currency"),
|
||||||
|
"balance": flt(
|
||||||
|
d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
|
||||||
|
),
|
||||||
|
"exchange_rate": 0,
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
|
"reference_type": "Exchange Rate Revaluation",
|
||||||
|
"reference_name": self.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Account Currency has balance
|
||||||
|
if d.get("balance_in_account_currency") and not d.get("new_balance_in_account_currency"):
|
||||||
|
dr_or_cr = (
|
||||||
|
"credit_in_account_currency"
|
||||||
|
if d.get("balance_in_account_currency") > 0
|
||||||
|
else "debit_in_account_currency"
|
||||||
|
)
|
||||||
|
reverse_dr_or_cr = (
|
||||||
|
"debit_in_account_currency"
|
||||||
|
if dr_or_cr == "credit_in_account_currency"
|
||||||
|
else "credit_in_account_currency"
|
||||||
|
)
|
||||||
|
journal_account.update(
|
||||||
|
{
|
||||||
|
dr_or_cr: flt(
|
||||||
|
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||||
|
),
|
||||||
|
reverse_dr_or_cr: 0,
|
||||||
|
"debit": 0,
|
||||||
|
"credit": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif d.get("balance_in_base_currency") and not d.get("new_balance_in_base_currency"):
|
||||||
|
# Base currency has balance
|
||||||
|
dr_or_cr = "credit" if d.get("balance_in_base_currency") > 0 else "debit"
|
||||||
|
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
||||||
|
journal_account.update(
|
||||||
|
{
|
||||||
|
dr_or_cr: flt(
|
||||||
|
abs(d.get("balance_in_base_currency")), d.precision("balance_in_base_currency")
|
||||||
|
),
|
||||||
|
reverse_dr_or_cr: 0,
|
||||||
|
"debit_in_account_currency": 0,
|
||||||
|
"credit_in_account_currency": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
journal_entry_accounts.append(journal_account)
|
||||||
|
|
||||||
|
journal_entry_accounts.append(
|
||||||
|
{
|
||||||
|
"account": unrealized_exchange_gain_loss_account,
|
||||||
|
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
|
||||||
|
"debit": abs(self.gain_loss_booked) if self.gain_loss_booked < 0 else 0,
|
||||||
|
"credit": abs(self.gain_loss_booked) if self.gain_loss_booked > 0 else 0,
|
||||||
|
"debit_in_account_currency": abs(self.gain_loss_booked) if self.gain_loss_booked < 0 else 0,
|
||||||
|
"credit_in_account_currency": self.gain_loss_booked if self.gain_loss_booked > 0 else 0,
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
|
"exchange_rate": 1,
|
||||||
|
"reference_type": "Exchange Rate Revaluation",
|
||||||
|
"reference_name": self.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
journal_entry.set("accounts", journal_entry_accounts)
|
||||||
|
journal_entry.set_total_debit_credit()
|
||||||
|
journal_entry.save()
|
||||||
|
return journal_entry
|
||||||
|
|
||||||
|
def make_jv_for_revaluation(self):
|
||||||
|
if self.gain_loss_unbooked == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
accounts = [x for x in self.accounts if not x.zero_balance]
|
||||||
|
if not accounts:
|
||||||
|
return
|
||||||
|
|
||||||
|
unrealized_exchange_gain_loss_account = self.get_for_unrealized_gain_loss_account()
|
||||||
|
|
||||||
journal_entry = frappe.new_doc("Journal Entry")
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
journal_entry.voucher_type = "Exchange Rate Revaluation"
|
journal_entry.voucher_type = "Exchange Rate Revaluation"
|
||||||
@@ -154,7 +398,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
journal_entry.multi_currency = 1
|
journal_entry.multi_currency = 1
|
||||||
|
|
||||||
journal_entry_accounts = []
|
journal_entry_accounts = []
|
||||||
for d in self.accounts:
|
for d in accounts:
|
||||||
dr_or_cr = (
|
dr_or_cr = (
|
||||||
"debit_in_account_currency"
|
"debit_in_account_currency"
|
||||||
if d.get("balance_in_account_currency") > 0
|
if d.get("balance_in_account_currency") > 0
|
||||||
@@ -179,6 +423,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
dr_or_cr: flt(
|
dr_or_cr: flt(
|
||||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||||
),
|
),
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
|
"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
|
||||||
"reference_type": "Exchange Rate Revaluation",
|
"reference_type": "Exchange Rate Revaluation",
|
||||||
"reference_name": self.name,
|
"reference_name": self.name,
|
||||||
@@ -196,6 +441,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
reverse_dr_or_cr: flt(
|
reverse_dr_or_cr: flt(
|
||||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||||
),
|
),
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
|
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
|
||||||
"reference_type": "Exchange Rate Revaluation",
|
"reference_type": "Exchange Rate Revaluation",
|
||||||
"reference_name": self.name,
|
"reference_name": self.name,
|
||||||
@@ -206,8 +452,11 @@ class ExchangeRateRevaluation(Document):
|
|||||||
{
|
{
|
||||||
"account": unrealized_exchange_gain_loss_account,
|
"account": unrealized_exchange_gain_loss_account,
|
||||||
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
|
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
|
||||||
"debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
|
"debit_in_account_currency": abs(self.gain_loss_unbooked)
|
||||||
"credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
|
if self.gain_loss_unbooked < 0
|
||||||
|
else 0,
|
||||||
|
"credit_in_account_currency": self.gain_loss_unbooked if self.gain_loss_unbooked > 0 else 0,
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
"exchange_rate": 1,
|
"exchange_rate": 1,
|
||||||
"reference_type": "Exchange Rate Revaluation",
|
"reference_type": "Exchange Rate Revaluation",
|
||||||
"reference_name": self.name,
|
"reference_name": self.name,
|
||||||
@@ -217,38 +466,91 @@ class ExchangeRateRevaluation(Document):
|
|||||||
journal_entry.set("accounts", journal_entry_accounts)
|
journal_entry.set("accounts", journal_entry_accounts)
|
||||||
journal_entry.set_amounts_in_company_currency()
|
journal_entry.set_amounts_in_company_currency()
|
||||||
journal_entry.set_total_debit_credit()
|
journal_entry.set_total_debit_credit()
|
||||||
return journal_entry.as_dict()
|
journal_entry.save()
|
||||||
|
return journal_entry
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
|
||||||
|
"""
|
||||||
|
Use last GL entry to calculate exchange rate
|
||||||
|
"""
|
||||||
|
last_exchange_rate = None
|
||||||
|
if company and account:
|
||||||
|
gl = qb.DocType("GL Entry")
|
||||||
|
|
||||||
|
# build conditions
|
||||||
|
conditions = []
|
||||||
|
conditions.append(gl.company == company)
|
||||||
|
conditions.append(gl.account == account)
|
||||||
|
conditions.append(gl.is_cancelled == 0)
|
||||||
|
if party_type:
|
||||||
|
conditions.append(gl.party_type == party_type)
|
||||||
|
if party:
|
||||||
|
conditions.append(gl.party == party)
|
||||||
|
|
||||||
|
voucher_type, voucher_no = (
|
||||||
|
qb.from_(gl)
|
||||||
|
.select(gl.voucher_type, gl.voucher_no)
|
||||||
|
.where(Criterion.all(conditions))
|
||||||
|
.orderby(gl.posting_date, order=Order.desc)
|
||||||
|
.limit(1)
|
||||||
|
.run()[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
last_exchange_rate = (
|
||||||
|
qb.from_(gl)
|
||||||
|
.select((gl.debit - gl.credit) / (gl.debit_in_account_currency - gl.credit_in_account_currency))
|
||||||
|
.where(
|
||||||
|
(gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no) & (gl.account == account)
|
||||||
|
)
|
||||||
|
.orderby(gl.posting_date, order=Order.desc)
|
||||||
|
.limit(1)
|
||||||
|
.run()[0][0]
|
||||||
|
)
|
||||||
|
|
||||||
|
return last_exchange_rate
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_details(account, company, posting_date, party_type=None, party=None):
|
def get_account_details(company, posting_date, account, party_type=None, party=None):
|
||||||
account_currency, account_type = frappe.db.get_value(
|
if not (company and posting_date):
|
||||||
|
frappe.throw(_("Company and Posting Date is mandatory"))
|
||||||
|
|
||||||
|
account_currency, account_type = frappe.get_cached_value(
|
||||||
"Account", account, ["account_currency", "account_type"]
|
"Account", account, ["account_currency", "account_type"]
|
||||||
)
|
)
|
||||||
|
|
||||||
if account_type in ["Receivable", "Payable"] and not (party_type and party):
|
if account_type in ["Receivable", "Payable"] and not (party_type and party):
|
||||||
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
||||||
|
|
||||||
account_details = {}
|
account_details = {}
|
||||||
company_currency = erpnext.get_company_currency(company)
|
company_currency = erpnext.get_company_currency(company)
|
||||||
balance = get_balance_on(
|
|
||||||
account, date=posting_date, party_type=party_type, party=party, in_account_currency=False
|
account_details = {
|
||||||
|
"account_currency": account_currency,
|
||||||
|
}
|
||||||
|
account_balance = ExchangeRateRevaluation.get_account_balance_from_gle(
|
||||||
|
company=company, posting_date=posting_date, account=account, party_type=party_type, party=party
|
||||||
)
|
)
|
||||||
if balance:
|
|
||||||
balance_in_account_currency = get_balance_on(
|
if account_balance and (
|
||||||
account, date=posting_date, party_type=party_type, party=party
|
account_balance[0].balance or account_balance[0].balance_in_account_currency
|
||||||
|
):
|
||||||
|
account_with_new_balance = ExchangeRateRevaluation.calculate_new_account_balance(
|
||||||
|
company, posting_date, account_balance
|
||||||
)
|
)
|
||||||
current_exchange_rate = (
|
row = account_with_new_balance[0]
|
||||||
balance / balance_in_account_currency if balance_in_account_currency else 0
|
account_details.update(
|
||||||
|
{
|
||||||
|
"balance_in_base_currency": row["balance_in_base_currency"],
|
||||||
|
"balance_in_account_currency": row["balance_in_account_currency"],
|
||||||
|
"current_exchange_rate": row["current_exchange_rate"],
|
||||||
|
"new_exchange_rate": row["new_exchange_rate"],
|
||||||
|
"new_balance_in_base_currency": row["new_balance_in_base_currency"],
|
||||||
|
"new_balance_in_account_currency": row["new_balance_in_account_currency"],
|
||||||
|
"zero_balance": row["zero_balance"],
|
||||||
|
"gain_loss": row["gain_loss"],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
|
||||||
new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
|
|
||||||
account_details = {
|
|
||||||
"account_currency": account_currency,
|
|
||||||
"balance_in_base_currency": balance,
|
|
||||||
"balance_in_account_currency": balance_in_account_currency,
|
|
||||||
"current_exchange_rate": current_exchange_rate,
|
|
||||||
"new_exchange_rate": new_exchange_rate,
|
|
||||||
"new_balance_in_base_currency": new_balance_in_base_currency,
|
|
||||||
}
|
|
||||||
|
|
||||||
return account_details
|
return account_details
|
||||||
|
|||||||
@@ -1,475 +1,161 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-04-13 18:30:06.110433",
|
"creation": "2018-04-13 18:30:06.110433",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"account",
|
||||||
|
"party_type",
|
||||||
|
"party",
|
||||||
|
"column_break_2",
|
||||||
|
"account_currency",
|
||||||
|
"account_balances",
|
||||||
|
"balance_in_account_currency",
|
||||||
|
"column_break_46yz",
|
||||||
|
"new_balance_in_account_currency",
|
||||||
|
"balances",
|
||||||
|
"current_exchange_rate",
|
||||||
|
"column_break_xown",
|
||||||
|
"new_exchange_rate",
|
||||||
|
"column_break_9",
|
||||||
|
"balance_in_base_currency",
|
||||||
|
"column_break_ukce",
|
||||||
|
"new_balance_in_base_currency",
|
||||||
|
"section_break_ngrs",
|
||||||
|
"gain_loss",
|
||||||
|
"zero_balance"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Account",
|
"label": "Account",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "party_type",
|
"fieldname": "party_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Party Type",
|
"label": "Party Type",
|
||||||
"length": 0,
|
"options": "DocType"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "DocType",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "party",
|
"fieldname": "party",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Party",
|
"label": "Party",
|
||||||
"length": 0,
|
"options": "party_type"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "party_type",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_2",
|
"fieldname": "column_break_2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "account_currency",
|
"fieldname": "account_currency",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Account Currency",
|
"label": "Account Currency",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Currency",
|
"options": "Currency",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "balance_in_account_currency",
|
"fieldname": "balance_in_account_currency",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Balance In Account Currency",
|
"label": "Balance In Account Currency",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "account_currency",
|
"options": "account_currency",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "balances",
|
"fieldname": "balances",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "current_exchange_rate",
|
"fieldname": "current_exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Current Exchange Rate",
|
"label": "Current Exchange Rate",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "balance_in_base_currency",
|
"fieldname": "balance_in_base_currency",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Balance In Base Currency",
|
"label": "Balance In Base Currency",
|
||||||
"length": 0,
|
"options": "Company:company:default_currency",
|
||||||
"no_copy": 0,
|
"read_only": 1
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_9",
|
"fieldname": "column_break_9",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Section Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "new_exchange_rate",
|
"fieldname": "new_exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "New Exchange Rate",
|
"label": "New Exchange Rate",
|
||||||
"length": 0,
|
"reqd": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "new_balance_in_base_currency",
|
"fieldname": "new_balance_in_base_currency",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "New Balance In Base Currency",
|
"label": "New Balance In Base Currency",
|
||||||
"length": 0,
|
"options": "Company:company:default_currency",
|
||||||
"no_copy": 0,
|
"read_only": 1
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "gain_loss",
|
"fieldname": "gain_loss",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Gain/Loss",
|
"label": "Gain/Loss",
|
||||||
"length": 0,
|
"options": "Company:company:default_currency",
|
||||||
"no_copy": 0,
|
"read_only": 1
|
||||||
"permlevel": 0,
|
},
|
||||||
"precision": "",
|
{
|
||||||
"print_hide": 0,
|
"default": "0",
|
||||||
"print_hide_if_no_value": 0,
|
"description": "This Account has '0' balance in either Base Currency or Account Currency",
|
||||||
"read_only": 1,
|
"fieldname": "zero_balance",
|
||||||
"remember_last_selected_value": 0,
|
"fieldtype": "Check",
|
||||||
"report_hide": 0,
|
"label": "Zero Balance"
|
||||||
"reqd": 0,
|
},
|
||||||
"search_index": 0,
|
{
|
||||||
"set_only_once": 0,
|
"fieldname": "new_balance_in_account_currency",
|
||||||
"translatable": 0,
|
"fieldtype": "Currency",
|
||||||
"unique": 0
|
"label": "New Balance In Account Currency",
|
||||||
|
"options": "account_currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "account_balances",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_46yz",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_xown",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_ukce",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_ngrs",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2019-06-26 18:57:51.762345",
|
"modified": "2022-12-29 19:38:52.915295",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Exchange Rate Revaluation Account",
|
"name": "Exchange Rate Revaluation Account",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"states": [],
|
||||||
"track_seen": 0,
|
"track_changes": 1
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -9,10 +9,6 @@ from frappe.model.document import Document
|
|||||||
from frappe.utils import add_days, add_years, cstr, getdate
|
from frappe.utils import add_days, add_years, cstr, getdate
|
||||||
|
|
||||||
|
|
||||||
class FiscalYearIncorrectDate(frappe.ValidationError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FiscalYear(Document):
|
class FiscalYear(Document):
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_as_default(self):
|
def set_as_default(self):
|
||||||
@@ -53,23 +49,18 @@ class FiscalYear(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
|
self.validate_from_to_dates("year_start_date", "year_end_date")
|
||||||
if self.is_short_year:
|
if self.is_short_year:
|
||||||
# Fiscal Year can be shorter than one year, in some jurisdictions
|
# Fiscal Year can be shorter than one year, in some jurisdictions
|
||||||
# under certain circumstances. For example, in the USA and Germany.
|
# under certain circumstances. For example, in the USA and Germany.
|
||||||
return
|
return
|
||||||
|
|
||||||
if getdate(self.year_start_date) > getdate(self.year_end_date):
|
|
||||||
frappe.throw(
|
|
||||||
_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
|
|
||||||
FiscalYearIncorrectDate,
|
|
||||||
)
|
|
||||||
|
|
||||||
date = getdate(self.year_start_date) + relativedelta(years=1) - relativedelta(days=1)
|
date = getdate(self.year_start_date) + relativedelta(years=1) - relativedelta(days=1)
|
||||||
|
|
||||||
if getdate(self.year_end_date) != date:
|
if getdate(self.year_end_date) != date:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
|
_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
|
||||||
FiscalYearIncorrectDate,
|
frappe.exceptions.InvalidDates,
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
@@ -169,5 +160,6 @@ def auto_create_fiscal_year():
|
|||||||
|
|
||||||
|
|
||||||
def get_from_and_to_date(fiscal_year):
|
def get_from_and_to_date(fiscal_year):
|
||||||
fields = ["year_start_date as from_date", "year_end_date as to_date"]
|
fields = ["year_start_date", "year_end_date"]
|
||||||
return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1)
|
cached_results = frappe.get_cached_value("Fiscal Year", fiscal_year, fields, as_dict=1)
|
||||||
|
return dict(from_date=cached_results.year_start_date, to_date=cached_results.year_end_date)
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import now_datetime
|
from frappe.utils import now_datetime
|
||||||
|
|
||||||
from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate
|
|
||||||
|
|
||||||
test_ignore = ["Company"]
|
test_ignore = ["Company"]
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ class TestFiscalYear(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
|
self.assertRaises(frappe.exceptions.InvalidDates, fy.insert)
|
||||||
|
|
||||||
|
|
||||||
def test_record_generator():
|
def test_record_generator():
|
||||||
@@ -35,8 +33,8 @@ def test_record_generator():
|
|||||||
"doctype": "Fiscal Year",
|
"doctype": "Fiscal Year",
|
||||||
"year": "_Test Short Fiscal Year 2011",
|
"year": "_Test Short Fiscal Year 2011",
|
||||||
"is_short_year": 1,
|
"is_short_year": 1,
|
||||||
"year_end_date": "2011-04-01",
|
"year_start_date": "2011-04-01",
|
||||||
"year_start_date": "2011-12-31",
|
"year_end_date": "2011-12-31",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class GLEntry(Document):
|
|||||||
self.validate_and_set_fiscal_year()
|
self.validate_and_set_fiscal_year()
|
||||||
self.pl_must_have_cost_center()
|
self.pl_must_have_cost_center()
|
||||||
|
|
||||||
if not self.flags.from_repost:
|
if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
|
||||||
self.check_mandatory()
|
self.check_mandatory()
|
||||||
self.validate_cost_center()
|
self.validate_cost_center()
|
||||||
self.check_pl_account()
|
self.check_pl_account()
|
||||||
@@ -51,14 +51,14 @@ class GLEntry(Document):
|
|||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
adv_adj = self.flags.adv_adj
|
adv_adj = self.flags.adv_adj
|
||||||
if not self.flags.from_repost:
|
if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
|
||||||
self.validate_account_details(adv_adj)
|
self.validate_account_details(adv_adj)
|
||||||
self.validate_dimensions_for_pl_and_bs()
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
self.validate_allowed_dimensions()
|
self.validate_allowed_dimensions()
|
||||||
validate_balance_type(self.account, adv_adj)
|
validate_balance_type(self.account, adv_adj)
|
||||||
validate_frozen_account(self.account, adv_adj)
|
validate_frozen_account(self.account, adv_adj)
|
||||||
|
|
||||||
if frappe.db.get_value("Account", self.account, "account_type") not in [
|
if frappe.get_cached_value("Account", self.account, "account_type") not in [
|
||||||
"Receivable",
|
"Receivable",
|
||||||
"Payable",
|
"Payable",
|
||||||
]:
|
]:
|
||||||
@@ -95,7 +95,15 @@ class GLEntry(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Zero value transaction is not allowed
|
# Zero value transaction is not allowed
|
||||||
if not (flt(self.debit, self.precision("debit")) or flt(self.credit, self.precision("credit"))):
|
if not (
|
||||||
|
flt(self.debit, self.precision("debit"))
|
||||||
|
or flt(self.credit, self.precision("credit"))
|
||||||
|
or (
|
||||||
|
self.voucher_type == "Journal Entry"
|
||||||
|
and frappe.get_cached_value("Journal Entry", self.voucher_no, "voucher_type")
|
||||||
|
== "Exchange Gain Or Loss"
|
||||||
|
)
|
||||||
|
):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("{0} {1}: Either debit or credit amount is required for {2}").format(
|
_("{0} {1}: Either debit or credit amount is required for {2}").format(
|
||||||
self.voucher_type, self.voucher_no, self.account
|
self.voucher_type, self.voucher_no, self.account
|
||||||
@@ -120,7 +128,7 @@ class GLEntry(Document):
|
|||||||
frappe.throw(msg, title=_("Missing Cost Center"))
|
frappe.throw(msg, title=_("Missing Cost Center"))
|
||||||
|
|
||||||
def validate_dimensions_for_pl_and_bs(self):
|
def validate_dimensions_for_pl_and_bs(self):
|
||||||
account_type = frappe.db.get_value("Account", self.account, "report_type")
|
account_type = frappe.get_cached_value("Account", self.account, "report_type")
|
||||||
|
|
||||||
for dimension in get_checks_for_pl_and_bs_accounts():
|
for dimension in get_checks_for_pl_and_bs_accounts():
|
||||||
if (
|
if (
|
||||||
@@ -188,7 +196,7 @@ class GLEntry(Document):
|
|||||||
def check_pl_account(self):
|
def check_pl_account(self):
|
||||||
if (
|
if (
|
||||||
self.is_opening == "Yes"
|
self.is_opening == "Yes"
|
||||||
and frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss"
|
and frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss"
|
||||||
and not self.is_cancelled
|
and not self.is_cancelled
|
||||||
):
|
):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
@@ -281,7 +289,7 @@ class GLEntry(Document):
|
|||||||
|
|
||||||
def validate_balance_type(account, adv_adj=False):
|
def validate_balance_type(account, adv_adj=False):
|
||||||
if not adv_adj and account:
|
if not adv_adj and account:
|
||||||
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
|
balance_must_be = frappe.get_cached_value("Account", account, "balance_must_be")
|
||||||
if balance_must_be:
|
if balance_must_be:
|
||||||
balance = frappe.db.sql(
|
balance = frappe.db.sql(
|
||||||
"""select sum(debit) - sum(credit)
|
"""select sum(debit) - sum(credit)
|
||||||
@@ -366,7 +374,7 @@ def update_outstanding_amt(
|
|||||||
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
||||||
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
|
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
|
||||||
|
|
||||||
# Didn't use db_set for optimisation purpose
|
# Didn't use db_set for optimization purpose
|
||||||
ref_doc.outstanding_amount = bal
|
ref_doc.outstanding_amount = bal
|
||||||
frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)
|
frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)
|
||||||
|
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
{
|
|
||||||
"actions": [],
|
|
||||||
"creation": "2018-01-02 15:48:58.768352",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"field_order": [
|
|
||||||
"company",
|
|
||||||
"cgst_account",
|
|
||||||
"sgst_account",
|
|
||||||
"igst_account",
|
|
||||||
"cess_account",
|
|
||||||
"utgst_account",
|
|
||||||
"is_reverse_charge_account"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"columns": 1,
|
|
||||||
"fieldname": "company",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"options": "Company",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "cgst_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "CGST Account",
|
|
||||||
"options": "Account",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "sgst_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "SGST Account",
|
|
||||||
"options": "Account",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "igst_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "IGST Account",
|
|
||||||
"options": "Account",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "cess_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "CESS Account",
|
|
||||||
"options": "Account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"columns": 1,
|
|
||||||
"default": "0",
|
|
||||||
"fieldname": "is_reverse_charge_account",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Is Reverse Charge Account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "utgst_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "UTGST Account",
|
|
||||||
"options": "Account"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"index_web_pages_for_search": 1,
|
|
||||||
"istable": 1,
|
|
||||||
"links": [],
|
|
||||||
"modified": "2022-04-07 12:59:14.039768",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "GST Account",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 1,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"states": [],
|
|
||||||
"track_changes": 1
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
|
|
||||||
class GSTAccount(Document):
|
|
||||||
pass
|
|
||||||
@@ -21,7 +21,7 @@ class ItemTaxTemplate(Document):
|
|||||||
check_list = []
|
check_list = []
|
||||||
for d in self.get("taxes"):
|
for d in self.get("taxes"):
|
||||||
if d.tax_type:
|
if d.tax_type:
|
||||||
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
|
account_type = frappe.get_cached_value("Account", d.tax_type, "account_type")
|
||||||
|
|
||||||
if account_type not in [
|
if account_type not in [
|
||||||
"Tax",
|
"Tax",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry");
|
|||||||
frappe.ui.form.on("Journal Entry", {
|
frappe.ui.form.on("Journal Entry", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("bank_account", "account", "account");
|
frm.add_fetch("bank_account", "account", "account");
|
||||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice'];
|
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry'];
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
@@ -149,22 +149,6 @@ frappe.ui.form.on("Journal Entry", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if(frm.doc.voucher_type=="Opening Entry") {
|
|
||||||
return frappe.call({
|
|
||||||
type:"GET",
|
|
||||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
|
|
||||||
args: {
|
|
||||||
"company": frm.doc.company
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
frappe.model.clear_table(frm.doc, "accounts");
|
|
||||||
if(r.message) {
|
|
||||||
update_jv_details(frm.doc, r.message);
|
|
||||||
}
|
|
||||||
cur_frm.set_value("is_opening", "Yes");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -189,8 +173,8 @@ frappe.ui.form.on("Journal Entry", {
|
|||||||
var update_jv_details = function(doc, r) {
|
var update_jv_details = function(doc, r) {
|
||||||
$.each(r, function(i, d) {
|
$.each(r, function(i, d) {
|
||||||
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
|
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
|
||||||
row.account = d.account;
|
frappe.model.set_value(row.doctype, row.name, "account", d.account)
|
||||||
row.balance = d.balance;
|
frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
|
||||||
});
|
});
|
||||||
refresh_field("accounts");
|
refresh_field("accounts");
|
||||||
}
|
}
|
||||||
@@ -240,25 +224,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
|||||||
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
|
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
|
||||||
var jvd = frappe.get_doc(cdt, cdn);
|
var jvd = frappe.get_doc(cdt, cdn);
|
||||||
|
|
||||||
// expense claim
|
|
||||||
if(jvd.reference_type==="Expense Claim") {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'total_sanctioned_amount': ['>', 0],
|
|
||||||
'status': ['!=', 'Paid'],
|
|
||||||
'docstatus': 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(jvd.reference_type==="Employee Advance") {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'docstatus': 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// journal entry
|
// journal entry
|
||||||
if(jvd.reference_type==="Journal Entry") {
|
if(jvd.reference_type==="Journal Entry") {
|
||||||
frappe.model.validate_missing(jvd, "account");
|
frappe.model.validate_missing(jvd, "account");
|
||||||
@@ -271,13 +236,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// payroll entry
|
|
||||||
if(jvd.reference_type==="Payroll Entry") {
|
|
||||||
return {
|
|
||||||
query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var out = {
|
var out = {
|
||||||
filters: [
|
filters: [
|
||||||
[jvd.reference_type, "docstatus", "=", 1]
|
[jvd.reference_type, "docstatus", "=", 1]
|
||||||
@@ -295,9 +253,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
|||||||
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
|
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
|
||||||
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
|
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
|
||||||
|
|
||||||
if (in_list(['Debit Note', 'Credit Note'], doc.voucher_type)) {
|
|
||||||
out.filters.push([jvd.reference_type, "is_return", "=", 1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) {
|
if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) {
|
||||||
@@ -354,8 +309,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_outstanding(doctype, docname, company, child, due_date) {
|
get_outstanding(doctype, docname, company, child) {
|
||||||
var me = this;
|
|
||||||
var args = {
|
var args = {
|
||||||
"doctype": doctype,
|
"doctype": doctype,
|
||||||
"docname": docname,
|
"docname": docname,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
"label": "Entry Type",
|
"label": "Entry Type",
|
||||||
"oldfieldname": "voucher_type",
|
"oldfieldname": "voucher_type",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense",
|
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
@@ -538,7 +538,7 @@
|
|||||||
"idx": 176,
|
"idx": 176,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-04-06 17:18:46.865259",
|
"modified": "2023-01-17 12:53:53.280620",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry",
|
"name": "Journal Entry",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import json
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, msgprint, scrub
|
from frappe import _, msgprint, scrub
|
||||||
from frappe.utils import cint, cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
|
from frappe.utils import cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
|
||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
|
from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
|
||||||
@@ -23,8 +23,10 @@ from erpnext.accounts.utils import (
|
|||||||
get_stock_accounts,
|
get_stock_accounts,
|
||||||
get_stock_and_account_balance,
|
get_stock_and_account_balance,
|
||||||
)
|
)
|
||||||
|
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
|
||||||
|
get_depr_schedule,
|
||||||
|
)
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
|
||||||
|
|
||||||
|
|
||||||
class StockAccountInvalidTransaction(frappe.ValidationError):
|
class StockAccountInvalidTransaction(frappe.ValidationError):
|
||||||
@@ -35,9 +37,6 @@ class JournalEntry(AccountsController):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(JournalEntry, self).__init__(*args, **kwargs)
|
super(JournalEntry, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_feed(self):
|
|
||||||
return self.voucher_type
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.voucher_type == "Opening Entry":
|
if self.voucher_type == "Opening Entry":
|
||||||
self.is_opening = "Yes"
|
self.is_opening = "Yes"
|
||||||
@@ -66,7 +65,6 @@ class JournalEntry(AccountsController):
|
|||||||
self.set_against_account()
|
self.set_against_account()
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_print_format_fields()
|
self.set_print_format_fields()
|
||||||
self.validate_expense_claim()
|
|
||||||
self.validate_credit_debit_note()
|
self.validate_credit_debit_note()
|
||||||
self.validate_empty_accounts_table()
|
self.validate_empty_accounts_table()
|
||||||
self.set_account_and_party_balance()
|
self.set_account_and_party_balance()
|
||||||
@@ -83,27 +81,22 @@ class JournalEntry(AccountsController):
|
|||||||
self.check_credit_limit()
|
self.check_credit_limit()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
self.update_asset_value()
|
||||||
self.update_inter_company_jv()
|
self.update_inter_company_jv()
|
||||||
self.update_invoice_discounting()
|
self.update_invoice_discounting()
|
||||||
self.update_status_for_full_and_final_statement()
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||||
from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
|
|
||||||
|
|
||||||
unlink_ref_doc_from_payment_entries(self)
|
unlink_ref_doc_from_payment_entries(self)
|
||||||
unlink_ref_doc_from_salary_slip(self.name)
|
|
||||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||||
self.make_gl_entries(1)
|
self.make_gl_entries(1)
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
|
||||||
self.unlink_advance_entry_reference()
|
self.unlink_advance_entry_reference()
|
||||||
self.unlink_asset_reference()
|
self.unlink_asset_reference()
|
||||||
self.unlink_inter_company_jv()
|
self.unlink_inter_company_jv()
|
||||||
self.unlink_asset_adjustment_entry()
|
self.unlink_asset_adjustment_entry()
|
||||||
self.update_invoice_discounting()
|
self.update_invoice_discounting()
|
||||||
self.update_status_for_full_and_final_statement()
|
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
return self.pay_to_recd_from or self.accounts[0].account
|
return self.pay_to_recd_from or self.accounts[0].account
|
||||||
@@ -112,21 +105,13 @@ class JournalEntry(AccountsController):
|
|||||||
advance_paid = frappe._dict()
|
advance_paid = frappe._dict()
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.is_advance:
|
if d.is_advance:
|
||||||
if d.reference_type in ("Sales Order", "Purchase Order", "Employee Advance"):
|
if d.reference_type in frappe.get_hooks("advance_payment_doctypes"):
|
||||||
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
||||||
|
|
||||||
for voucher_type, order_list in advance_paid.items():
|
for voucher_type, order_list in advance_paid.items():
|
||||||
for voucher_no in list(set(order_list)):
|
for voucher_no in list(set(order_list)):
|
||||||
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
|
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
|
||||||
|
|
||||||
def update_status_for_full_and_final_statement(self):
|
|
||||||
for entry in self.accounts:
|
|
||||||
if entry.reference_type == "Full and Final Statement":
|
|
||||||
if self.docstatus == 1:
|
|
||||||
frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Paid")
|
|
||||||
elif self.docstatus == 2:
|
|
||||||
frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Unpaid")
|
|
||||||
|
|
||||||
def validate_inter_company_accounts(self):
|
def validate_inter_company_accounts(self):
|
||||||
if (
|
if (
|
||||||
self.voucher_type == "Inter Company Journal Entry"
|
self.voucher_type == "Inter Company Journal Entry"
|
||||||
@@ -200,7 +185,9 @@ class JournalEntry(AccountsController):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
|
tax_withholding_details, advance_taxes, voucher_wise_amount = get_party_tax_withholding_details(
|
||||||
|
inv, self.tax_withholding_category
|
||||||
|
)
|
||||||
|
|
||||||
if not tax_withholding_details:
|
if not tax_withholding_details:
|
||||||
return
|
return
|
||||||
@@ -239,6 +226,34 @@ class JournalEntry(AccountsController):
|
|||||||
for d in to_remove:
|
for d in to_remove:
|
||||||
self.remove(d)
|
self.remove(d)
|
||||||
|
|
||||||
|
def update_asset_value(self):
|
||||||
|
if self.voucher_type != "Depreciation Entry":
|
||||||
|
return
|
||||||
|
|
||||||
|
processed_assets = []
|
||||||
|
|
||||||
|
for d in self.get("accounts"):
|
||||||
|
if (
|
||||||
|
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
|
||||||
|
):
|
||||||
|
processed_assets.append(d.reference_name)
|
||||||
|
|
||||||
|
asset = frappe.db.get_value(
|
||||||
|
"Asset", d.reference_name, ["calculate_depreciation", "value_after_depreciation"], as_dict=1
|
||||||
|
)
|
||||||
|
|
||||||
|
if asset.calculate_depreciation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
depr_value = d.debit or d.credit
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Asset",
|
||||||
|
d.reference_name,
|
||||||
|
"value_after_depreciation",
|
||||||
|
asset.value_after_depreciation - depr_value,
|
||||||
|
)
|
||||||
|
|
||||||
def update_inter_company_jv(self):
|
def update_inter_company_jv(self):
|
||||||
if (
|
if (
|
||||||
self.voucher_type == "Inter Company Journal Entry"
|
self.voucher_type == "Inter Company Journal Entry"
|
||||||
@@ -297,19 +312,48 @@ class JournalEntry(AccountsController):
|
|||||||
d.db_update()
|
d.db_update()
|
||||||
|
|
||||||
def unlink_asset_reference(self):
|
def unlink_asset_reference(self):
|
||||||
|
if self.voucher_type != "Depreciation Entry":
|
||||||
|
return
|
||||||
|
|
||||||
|
processed_assets = []
|
||||||
|
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.reference_type == "Asset" and d.reference_name:
|
if (
|
||||||
|
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
|
||||||
|
):
|
||||||
|
processed_assets.append(d.reference_name)
|
||||||
|
|
||||||
asset = frappe.get_doc("Asset", d.reference_name)
|
asset = frappe.get_doc("Asset", d.reference_name)
|
||||||
for s in asset.get("schedules"):
|
|
||||||
if s.journal_entry == self.name:
|
|
||||||
s.db_set("journal_entry", None)
|
|
||||||
|
|
||||||
idx = cint(s.finance_book_id) or 1
|
if asset.calculate_depreciation:
|
||||||
finance_books = asset.get("finance_books")[idx - 1]
|
je_found = False
|
||||||
finance_books.value_after_depreciation += s.depreciation_amount
|
|
||||||
finance_books.db_update()
|
|
||||||
|
|
||||||
asset.set_status()
|
for row in asset.get("finance_books"):
|
||||||
|
if je_found:
|
||||||
|
break
|
||||||
|
|
||||||
|
depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book)
|
||||||
|
|
||||||
|
for s in depr_schedule or []:
|
||||||
|
if s.journal_entry == self.name:
|
||||||
|
s.db_set("journal_entry", None)
|
||||||
|
|
||||||
|
row.value_after_depreciation += s.depreciation_amount
|
||||||
|
row.db_update()
|
||||||
|
|
||||||
|
asset.set_status()
|
||||||
|
|
||||||
|
je_found = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
depr_value = d.debit or d.credit
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Asset",
|
||||||
|
d.reference_name,
|
||||||
|
"value_after_depreciation",
|
||||||
|
asset.value_after_depreciation + depr_value,
|
||||||
|
)
|
||||||
|
|
||||||
def unlink_inter_company_jv(self):
|
def unlink_inter_company_jv(self):
|
||||||
if (
|
if (
|
||||||
@@ -333,7 +377,7 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
def validate_party(self):
|
def validate_party(self):
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
account_type = frappe.db.get_value("Account", d.account, "account_type")
|
account_type = frappe.get_cached_value("Account", d.account, "account_type")
|
||||||
if account_type in ["Receivable", "Payable"]:
|
if account_type in ["Receivable", "Payable"]:
|
||||||
if not (d.party_type and d.party):
|
if not (d.party_type and d.party):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
@@ -396,7 +440,7 @@ class JournalEntry(AccountsController):
|
|||||||
def validate_against_jv(self):
|
def validate_against_jv(self):
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.reference_type == "Journal Entry":
|
if d.reference_type == "Journal Entry":
|
||||||
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
account_root_type = frappe.get_cached_value("Account", d.account, "root_type")
|
||||||
if account_root_type == "Asset" and flt(d.debit) > 0:
|
if account_root_type == "Asset" and flt(d.debit) > 0:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
@@ -606,28 +650,30 @@ class JournalEntry(AccountsController):
|
|||||||
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
|
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
|
||||||
else:
|
else:
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if flt(d.debit > 0):
|
if flt(d.debit) > 0:
|
||||||
accounts_debited.append(d.party or d.account)
|
accounts_debited.append(d.party or d.account)
|
||||||
if flt(d.credit) > 0:
|
if flt(d.credit) > 0:
|
||||||
accounts_credited.append(d.party or d.account)
|
accounts_credited.append(d.party or d.account)
|
||||||
|
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if flt(d.debit > 0):
|
if flt(d.debit) > 0:
|
||||||
d.against_account = ", ".join(list(set(accounts_credited)))
|
d.against_account = ", ".join(list(set(accounts_credited)))
|
||||||
if flt(d.credit > 0):
|
if flt(d.credit) > 0:
|
||||||
d.against_account = ", ".join(list(set(accounts_debited)))
|
d.against_account = ", ".join(list(set(accounts_debited)))
|
||||||
|
|
||||||
def validate_debit_credit_amount(self):
|
def validate_debit_credit_amount(self):
|
||||||
for d in self.get("accounts"):
|
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
|
||||||
if not flt(d.debit) and not flt(d.credit):
|
for d in self.get("accounts"):
|
||||||
frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx))
|
if not flt(d.debit) and not flt(d.credit):
|
||||||
|
frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx))
|
||||||
|
|
||||||
def validate_total_debit_and_credit(self):
|
def validate_total_debit_and_credit(self):
|
||||||
self.set_total_debit_credit()
|
self.set_total_debit_credit()
|
||||||
if self.difference:
|
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
|
||||||
frappe.throw(
|
if self.difference:
|
||||||
_("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference)
|
frappe.throw(
|
||||||
)
|
_("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference)
|
||||||
|
)
|
||||||
|
|
||||||
def set_total_debit_credit(self):
|
def set_total_debit_credit(self):
|
||||||
self.total_debit, self.total_credit, self.difference = 0, 0, 0
|
self.total_debit, self.total_credit, self.difference = 0, 0, 0
|
||||||
@@ -645,7 +691,7 @@ class JournalEntry(AccountsController):
|
|||||||
def validate_multi_currency(self):
|
def validate_multi_currency(self):
|
||||||
alternate_currency = []
|
alternate_currency = []
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
account = frappe.db.get_value(
|
account = frappe.get_cached_value(
|
||||||
"Account", d.account, ["account_currency", "account_type"], as_dict=1
|
"Account", d.account, ["account_currency", "account_type"], as_dict=1
|
||||||
)
|
)
|
||||||
if account:
|
if account:
|
||||||
@@ -665,16 +711,17 @@ class JournalEntry(AccountsController):
|
|||||||
self.set_exchange_rate()
|
self.set_exchange_rate()
|
||||||
|
|
||||||
def set_amounts_in_company_currency(self):
|
def set_amounts_in_company_currency(self):
|
||||||
for d in self.get("accounts"):
|
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
|
||||||
d.debit_in_account_currency = flt(
|
for d in self.get("accounts"):
|
||||||
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
d.debit_in_account_currency = flt(
|
||||||
)
|
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
||||||
d.credit_in_account_currency = flt(
|
)
|
||||||
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
d.credit_in_account_currency = flt(
|
||||||
)
|
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
||||||
|
)
|
||||||
|
|
||||||
d.debit = flt(d.debit_in_account_currency * flt(d.exchange_rate), d.precision("debit"))
|
d.debit = flt(d.debit_in_account_currency * flt(d.exchange_rate), d.precision("debit"))
|
||||||
d.credit = flt(d.credit_in_account_currency * flt(d.exchange_rate), d.precision("credit"))
|
d.credit = flt(d.credit_in_account_currency * flt(d.exchange_rate), d.precision("credit"))
|
||||||
|
|
||||||
def set_exchange_rate(self):
|
def set_exchange_rate(self):
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
@@ -773,10 +820,10 @@ class JournalEntry(AccountsController):
|
|||||||
pay_to_recd_from = d.party
|
pay_to_recd_from = d.party
|
||||||
|
|
||||||
if pay_to_recd_from and pay_to_recd_from == d.party:
|
if pay_to_recd_from and pay_to_recd_from == d.party:
|
||||||
party_amount += d.debit_in_account_currency or d.credit_in_account_currency
|
party_amount += flt(d.debit_in_account_currency) or flt(d.credit_in_account_currency)
|
||||||
party_account_currency = d.account_currency
|
party_account_currency = d.account_currency
|
||||||
|
|
||||||
elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
|
elif frappe.get_cached_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
|
||||||
bank_amount += d.debit_in_account_currency or d.credit_in_account_currency
|
bank_amount += d.debit_in_account_currency or d.credit_in_account_currency
|
||||||
bank_account_currency = d.account_currency
|
bank_account_currency = d.account_currency
|
||||||
|
|
||||||
@@ -803,7 +850,7 @@ class JournalEntry(AccountsController):
|
|||||||
def build_gl_map(self):
|
def build_gl_map(self):
|
||||||
gl_map = []
|
gl_map = []
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if d.debit or d.credit:
|
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
|
||||||
r = [d.user_remark, self.remark]
|
r = [d.user_remark, self.remark]
|
||||||
r = [x for x in r if x]
|
r = [x for x in r if x]
|
||||||
remarks = "\n".join(r)
|
remarks = "\n".join(r)
|
||||||
@@ -851,7 +898,7 @@ class JournalEntry(AccountsController):
|
|||||||
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_balance(self):
|
def get_balance(self, difference_account=None):
|
||||||
if not self.get("accounts"):
|
if not self.get("accounts"):
|
||||||
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
||||||
else:
|
else:
|
||||||
@@ -866,7 +913,13 @@ class JournalEntry(AccountsController):
|
|||||||
blank_row = d
|
blank_row = d
|
||||||
|
|
||||||
if not blank_row:
|
if not blank_row:
|
||||||
blank_row = self.append("accounts", {})
|
blank_row = self.append(
|
||||||
|
"accounts",
|
||||||
|
{
|
||||||
|
"account": difference_account,
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
blank_row.exchange_rate = 1
|
blank_row.exchange_rate = 1
|
||||||
if diff > 0:
|
if diff > 0:
|
||||||
@@ -935,29 +988,6 @@ class JournalEntry(AccountsController):
|
|||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_expense_claim(self):
|
|
||||||
for d in self.accounts:
|
|
||||||
if d.reference_type == "Expense Claim" and d.reference_name:
|
|
||||||
doc = frappe.get_doc("Expense Claim", d.reference_name)
|
|
||||||
if self.docstatus == 2:
|
|
||||||
update_reimbursed_amount(doc, -1 * d.debit)
|
|
||||||
else:
|
|
||||||
update_reimbursed_amount(doc, d.debit)
|
|
||||||
|
|
||||||
def validate_expense_claim(self):
|
|
||||||
for d in self.accounts:
|
|
||||||
if d.reference_type == "Expense Claim":
|
|
||||||
sanctioned_amount, reimbursed_amount = frappe.db.get_value(
|
|
||||||
"Expense Claim", d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed")
|
|
||||||
)
|
|
||||||
pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount)
|
|
||||||
if d.debit > pending_amount:
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}"
|
|
||||||
).format(d.idx, d.reference_name, pending_amount)
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_credit_debit_note(self):
|
def validate_credit_debit_note(self):
|
||||||
if self.stock_entry:
|
if self.stock_entry:
|
||||||
if frappe.db.get_value("Stock Entry", self.stock_entry, "docstatus") != 1:
|
if frappe.db.get_value("Stock Entry", self.stock_entry, "docstatus") != 1:
|
||||||
@@ -1024,7 +1054,7 @@ def get_default_bank_cash_account(company, account_type=None, mode_of_payment=No
|
|||||||
account = account_list[0].name
|
account = account_list[0].name
|
||||||
|
|
||||||
if account:
|
if account:
|
||||||
account_details = frappe.db.get_value(
|
account_details = frappe.get_cached_value(
|
||||||
"Account", account, ["account_currency", "account_type"], as_dict=1
|
"Account", account, ["account_currency", "account_type"], as_dict=1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1153,7 +1183,7 @@ def get_payment_entry(ref_doc, args):
|
|||||||
"party_type": args.get("party_type"),
|
"party_type": args.get("party_type"),
|
||||||
"party": ref_doc.get(args.get("party_type").lower()),
|
"party": ref_doc.get(args.get("party_type").lower()),
|
||||||
"cost_center": cost_center,
|
"cost_center": cost_center,
|
||||||
"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
|
"account_type": frappe.get_cached_value("Account", args.get("party_account"), "account_type"),
|
||||||
"account_currency": args.get("party_account_currency")
|
"account_currency": args.get("party_account_currency")
|
||||||
or get_account_currency(args.get("party_account")),
|
or get_account_currency(args.get("party_account")),
|
||||||
"balance": get_balance_on(args.get("party_account")),
|
"balance": get_balance_on(args.get("party_account")),
|
||||||
@@ -1204,24 +1234,6 @@ def get_payment_entry(ref_doc, args):
|
|||||||
return je if args.get("journal_entry") else je.as_dict()
|
return je if args.get("journal_entry") else je.as_dict()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_opening_accounts(company):
|
|
||||||
"""get all balance sheet accounts for opening entry"""
|
|
||||||
accounts = frappe.db.sql_list(
|
|
||||||
"""select
|
|
||||||
name from tabAccount
|
|
||||||
where
|
|
||||||
is_group=0 and report_type='Balance Sheet' and company={0} and
|
|
||||||
name not in (select distinct account from tabWarehouse where
|
|
||||||
account is not null and account != '')
|
|
||||||
order by name asc""".format(
|
|
||||||
frappe.db.escape(company)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||||
@@ -1265,6 +1277,7 @@ def get_outstanding(args):
|
|||||||
args = json.loads(args)
|
args = json.loads(args)
|
||||||
|
|
||||||
company_currency = erpnext.get_company_currency(args.get("company"))
|
company_currency = erpnext.get_company_currency(args.get("company"))
|
||||||
|
due_date = None
|
||||||
|
|
||||||
if args.get("doctype") == "Journal Entry":
|
if args.get("doctype") == "Journal Entry":
|
||||||
condition = " and party=%(party)s" if args.get("party") else ""
|
condition = " and party=%(party)s" if args.get("party") else ""
|
||||||
@@ -1289,10 +1302,12 @@ def get_outstanding(args):
|
|||||||
invoice = frappe.db.get_value(
|
invoice = frappe.db.get_value(
|
||||||
args["doctype"],
|
args["doctype"],
|
||||||
args["docname"],
|
args["docname"],
|
||||||
["outstanding_amount", "conversion_rate", scrub(party_type)],
|
["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"],
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
due_date = invoice.get("due_date")
|
||||||
|
|
||||||
exchange_rate = (
|
exchange_rate = (
|
||||||
invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
|
invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
|
||||||
)
|
)
|
||||||
@@ -1315,6 +1330,7 @@ def get_outstanding(args):
|
|||||||
"exchange_rate": exchange_rate,
|
"exchange_rate": exchange_rate,
|
||||||
"party_type": party_type,
|
"party_type": party_type,
|
||||||
"party": invoice.get(scrub(party_type)),
|
"party": invoice.get(scrub(party_type)),
|
||||||
|
"reference_due_date": due_date,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1334,7 +1350,7 @@ def get_party_account_and_balance(company, party_type, party, cost_center=None):
|
|||||||
"account": account,
|
"account": account,
|
||||||
"balance": account_balance,
|
"balance": account_balance,
|
||||||
"party_balance": party_balance,
|
"party_balance": party_balance,
|
||||||
"account_currency": frappe.db.get_value("Account", account, "account_currency"),
|
"account_currency": frappe.get_cached_value("Account", account, "account_currency"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1347,7 +1363,7 @@ def get_account_balance_and_party_type(
|
|||||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
|
|
||||||
company_currency = erpnext.get_company_currency(company)
|
company_currency = erpnext.get_company_currency(company)
|
||||||
account_details = frappe.db.get_value(
|
account_details = frappe.get_cached_value(
|
||||||
"Account", account, ["account_type", "account_currency"], as_dict=1
|
"Account", account, ["account_type", "account_currency"], as_dict=1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1400,7 +1416,7 @@ def get_exchange_rate(
|
|||||||
):
|
):
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
|
|
||||||
account_details = frappe.db.get_value(
|
account_details = frappe.get_cached_value(
|
||||||
"Account", account, ["account_type", "root_type", "account_currency", "company"], as_dict=1
|
"Account", account, ["account_type", "root_type", "account_currency", "company"], as_dict=1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
frappe.ui.form.on("Journal Entry", {
|
|
||||||
refresh: function(frm) {
|
|
||||||
frm.set_query('company_address', function(doc) {
|
|
||||||
if(!doc.company) {
|
|
||||||
frappe.throw(__('Please set Company'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
query: 'frappe.contacts.doctype.address.address.address_query',
|
|
||||||
filters: {
|
|
||||||
link_doctype: 'Company',
|
|
||||||
link_name: doc.company
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -202,6 +202,7 @@
|
|||||||
"fieldname": "reference_type",
|
"fieldname": "reference_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Reference Type",
|
"label": "Reference Type",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
|
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -209,13 +210,15 @@
|
|||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Reference Name",
|
"label": "Reference Name",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "reference_type"
|
"options": "reference_type"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
|
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
|
||||||
"fieldname": "reference_due_date",
|
"fieldname": "reference_due_date",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Date",
|
||||||
"label": "Reference Due Date"
|
"label": "Reference Due Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "project",
|
"fieldname": "project",
|
||||||
@@ -274,19 +277,22 @@
|
|||||||
"fieldname": "reference_detail_no",
|
"fieldname": "reference_detail_no",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Reference Detail No"
|
"label": "Reference Detail No",
|
||||||
|
"no_copy": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-08-30 21:27:32.200299",
|
"modified": "2022-10-26 20:03:10.906259",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry Account",
|
"name": "Journal Entry Account",
|
||||||
|
"naming_rule": "Random",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("Journal Entry Template", {
|
frappe.ui.form.on("Journal Entry Template", {
|
||||||
setup: function(frm) {
|
refresh: function(frm) {
|
||||||
frappe.model.set_default_values(frm.doc);
|
frappe.model.set_default_values(frm.doc);
|
||||||
|
|
||||||
frm.set_query("account" ,"accounts", function(){
|
frm.set_query("account" ,"accounts", function(){
|
||||||
@@ -45,21 +45,6 @@ frappe.ui.form.on("Journal Entry Template", {
|
|||||||
|
|
||||||
frm.trigger("clear_child");
|
frm.trigger("clear_child");
|
||||||
switch(frm.doc.voucher_type){
|
switch(frm.doc.voucher_type){
|
||||||
case "Opening Entry":
|
|
||||||
frm.set_value("is_opening", "Yes");
|
|
||||||
frappe.call({
|
|
||||||
type:"GET",
|
|
||||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
|
|
||||||
args: {
|
|
||||||
"company": frm.doc.company
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if(r.message) {
|
|
||||||
add_accounts(frm.doc, r.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "Bank Entry":
|
case "Bank Entry":
|
||||||
case "Cash Entry":
|
case "Cash Entry":
|
||||||
frappe.call({
|
frappe.call({
|
||||||
|
|||||||
@@ -4,22 +4,20 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils.background_jobs import is_job_queued
|
||||||
|
|
||||||
from erpnext.accounts.doctype.account.account import merge_account
|
from erpnext.accounts.doctype.account.account import merge_account
|
||||||
|
|
||||||
|
|
||||||
class LedgerMerge(Document):
|
class LedgerMerge(Document):
|
||||||
def start_merge(self):
|
def start_merge(self):
|
||||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
|
||||||
from frappe.utils.background_jobs import enqueue
|
from frappe.utils.background_jobs import enqueue
|
||||||
from frappe.utils.scheduler import is_scheduler_inactive
|
from frappe.utils.scheduler import is_scheduler_inactive
|
||||||
|
|
||||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
if is_scheduler_inactive() and not frappe.flags.in_test:
|
||||||
frappe.throw(_("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive"))
|
frappe.throw(_("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive"))
|
||||||
|
|
||||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
if not is_job_queued(self.name):
|
||||||
|
|
||||||
if self.name not in enqueued_jobs:
|
|
||||||
enqueue(
|
enqueue(
|
||||||
start_merge,
|
start_merge,
|
||||||
queue="default",
|
queue="default",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ModeofPayment(Document):
|
|||||||
def validate_accounts(self):
|
def validate_accounts(self):
|
||||||
for entry in self.accounts:
|
for entry in self.accounts:
|
||||||
"""Error when Company of Ledger account doesn't match with Company Selected"""
|
"""Error when Company of Ledger account doesn't match with Company Selected"""
|
||||||
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
|
if frappe.get_cached_value("Account", entry.default_account, "company") != entry.company:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Account {0} does not match with Company {1} in Mode of Account: {2}").format(
|
_("Account {0} does not match with Company {1} in Mode of Account: {2}").format(
|
||||||
entry.default_account, entry.company, self.name
|
entry.default_account, entry.company, self.name
|
||||||
|
|||||||
@@ -20,15 +20,14 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
|
|||||||
frm.dashboard.reset();
|
frm.dashboard.reset();
|
||||||
frm.doc.import_in_progress = true;
|
frm.doc.import_in_progress = true;
|
||||||
}
|
}
|
||||||
if (data.user != frappe.session.user) return;
|
|
||||||
if (data.count == data.total) {
|
if (data.count == data.total) {
|
||||||
setTimeout((title) => {
|
setTimeout(() => {
|
||||||
frm.doc.import_in_progress = false;
|
frm.doc.import_in_progress = false;
|
||||||
frm.clear_table("invoices");
|
frm.clear_table("invoices");
|
||||||
frm.refresh_fields();
|
frm.refresh_fields();
|
||||||
frm.page.clear_indicator();
|
frm.page.clear_indicator();
|
||||||
frm.dashboard.hide_progress(title);
|
frm.dashboard.hide_progress();
|
||||||
frappe.msgprint(__("Opening {0} Invoice created", [frm.doc.invoice_type]));
|
frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type]));
|
||||||
}, 1500, data.title);
|
}, 1500, data.title);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -51,13 +50,6 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
|
|||||||
method: "make_invoices",
|
method: "make_invoices",
|
||||||
freeze: 1,
|
freeze: 1,
|
||||||
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]),
|
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]),
|
||||||
callback: function(r) {
|
|
||||||
if (r.message.length == 1) {
|
|
||||||
frappe.msgprint(__("{0} Invoice created successfully.", [frm.doc.invoice_type]));
|
|
||||||
} else if (r.message.length < 50) {
|
|
||||||
frappe.msgprint(__("{0} Invoices created successfully.", [frm.doc.invoice_type]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe import _, scrub
|
from frappe import _, scrub
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import flt, nowdate
|
from frappe.utils import flt, nowdate
|
||||||
from frappe.utils.background_jobs import enqueue
|
from frappe.utils.background_jobs import enqueue, is_job_queued
|
||||||
|
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||||
get_accounting_dimensions,
|
get_accounting_dimensions,
|
||||||
@@ -207,14 +207,12 @@ class OpeningInvoiceCreationTool(Document):
|
|||||||
if len(invoices) < 50:
|
if len(invoices) < 50:
|
||||||
return start_import(invoices)
|
return start_import(invoices)
|
||||||
else:
|
else:
|
||||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
|
||||||
from frappe.utils.scheduler import is_scheduler_inactive
|
from frappe.utils.scheduler import is_scheduler_inactive
|
||||||
|
|
||||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
if is_scheduler_inactive() and not frappe.flags.in_test:
|
||||||
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
|
||||||
|
|
||||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
if not is_job_queued(self.name):
|
||||||
if self.name not in enqueued_jobs:
|
|
||||||
enqueue(
|
enqueue(
|
||||||
start_import,
|
start_import,
|
||||||
queue="default",
|
queue="default",
|
||||||
@@ -257,17 +255,15 @@ def start_import(invoices):
|
|||||||
|
|
||||||
|
|
||||||
def publish(index, total, doctype):
|
def publish(index, total, doctype):
|
||||||
if total < 50:
|
|
||||||
return
|
|
||||||
frappe.publish_realtime(
|
frappe.publish_realtime(
|
||||||
"opening_invoice_creation_progress",
|
"opening_invoice_creation_progress",
|
||||||
dict(
|
dict(
|
||||||
title=_("Opening Invoice Creation In Progress"),
|
title=_("Opening Invoice Creation In Progress"),
|
||||||
message=_("Creating {} out of {} {}").format(index + 1, total, doctype),
|
message=_("Creating {} out of {} {}").format(index + 1, total, doctype),
|
||||||
user=frappe.session.user,
|
|
||||||
count=index + 1,
|
count=index + 1,
|
||||||
total=total,
|
total=total,
|
||||||
),
|
),
|
||||||
|
user=frappe.session.user,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -110,8 +110,6 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
|
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
|
||||||
} else if (frm.doc.party_type == "Supplier") {
|
} else if (frm.doc.party_type == "Supplier") {
|
||||||
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
|
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
|
||||||
} else if (frm.doc.party_type == "Employee") {
|
|
||||||
var doctypes = ["Expense Claim", "Journal Entry"];
|
|
||||||
} else {
|
} else {
|
||||||
var doctypes = ["Journal Entry"];
|
var doctypes = ["Journal Entry"];
|
||||||
}
|
}
|
||||||
@@ -140,17 +138,12 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
const child = locals[cdt][cdn];
|
const child = locals[cdt][cdn];
|
||||||
const filters = {"docstatus": 1, "company": doc.company};
|
const filters = {"docstatus": 1, "company": doc.company};
|
||||||
const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
|
const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
|
||||||
'Purchase Order', 'Expense Claim', 'Fees', 'Dunning'];
|
'Purchase Order', 'Dunning'];
|
||||||
|
|
||||||
if (in_list(party_type_doctypes, child.reference_doctype)) {
|
if (in_list(party_type_doctypes, child.reference_doctype)) {
|
||||||
filters[doc.party_type.toLowerCase()] = doc.party;
|
filters[doc.party_type.toLowerCase()] = doc.party;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(child.reference_doctype == "Expense Claim") {
|
|
||||||
filters["docstatus"] = 1;
|
|
||||||
filters["is_paid"] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: filters
|
filters: filters
|
||||||
};
|
};
|
||||||
@@ -730,7 +723,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
c.payment_term = d.payment_term;
|
c.payment_term = d.payment_term;
|
||||||
c.allocated_amount = d.allocated_amount;
|
c.allocated_amount = d.allocated_amount;
|
||||||
|
|
||||||
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) {
|
if(!in_list(frm.events.get_order_doctypes(frm), d.voucher_type)) {
|
||||||
if(flt(d.outstanding_amount) > 0)
|
if(flt(d.outstanding_amount) > 0)
|
||||||
total_positive_outstanding += flt(d.outstanding_amount);
|
total_positive_outstanding += flt(d.outstanding_amount);
|
||||||
else
|
else
|
||||||
@@ -745,7 +738,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
} else {
|
} else {
|
||||||
c.exchange_rate = 1;
|
c.exchange_rate = 1;
|
||||||
}
|
}
|
||||||
if (in_list(['Sales Invoice', 'Purchase Invoice', "Expense Claim", "Fees"], d.reference_doctype)){
|
if (in_list(frm.events.get_invoice_doctypes(frm), d.reference_doctype)){
|
||||||
c.due_date = d.due_date;
|
c.due_date = d.due_date;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -776,6 +769,14 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get_order_doctypes: function(frm) {
|
||||||
|
return ["Sales Order", "Purchase Order"];
|
||||||
|
},
|
||||||
|
|
||||||
|
get_invoice_doctypes: function(frm) {
|
||||||
|
return ["Sales Invoice", "Purchase Invoice"];
|
||||||
|
},
|
||||||
|
|
||||||
allocate_party_amount_against_ref_docs: function(frm, paid_amount, paid_amount_change) {
|
allocate_party_amount_against_ref_docs: function(frm, paid_amount, paid_amount_change) {
|
||||||
var total_positive_outstanding_including_order = 0;
|
var total_positive_outstanding_including_order = 0;
|
||||||
var total_negative_outstanding = 0;
|
var total_negative_outstanding = 0;
|
||||||
@@ -946,14 +947,6 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
|
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(frm.doc.party_type=="Employee" &&
|
|
||||||
!in_list(["Expense Claim", "Journal Entry"], row.reference_doctype)
|
|
||||||
) {
|
|
||||||
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
|
|
||||||
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry", [row.idx]));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row) {
|
if (row) {
|
||||||
@@ -1098,7 +1091,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; });
|
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; });
|
||||||
|
|
||||||
frm.doc.paid_amount_after_tax = frm.doc.paid_amount;
|
frm.doc.paid_amount_after_tax = frm.doc.base_paid_amount;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1189,7 +1182,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
|
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
|
||||||
frm.doc.paid_amount_after_tax = flt(frm.doc.paid_amount/(1+cumulated_tax_fraction))
|
frm.doc.paid_amount_after_tax = flt(frm.doc.base_paid_amount/(1+cumulated_tax_fraction))
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1221,6 +1214,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.doc.total_taxes_and_charges = 0.0;
|
frm.doc.total_taxes_and_charges = 0.0;
|
||||||
frm.doc.base_total_taxes_and_charges = 0.0;
|
frm.doc.base_total_taxes_and_charges = 0.0;
|
||||||
|
|
||||||
|
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||||
let actual_tax_dict = {};
|
let actual_tax_dict = {};
|
||||||
|
|
||||||
// maintain actual tax rate based on idx
|
// maintain actual tax rate based on idx
|
||||||
@@ -1241,8 +1235,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tax.tax_amount = current_tax_amount;
|
// tax accounts are only in company currency
|
||||||
tax.base_tax_amount = tax.tax_amount * frm.doc.source_exchange_rate;
|
tax.base_tax_amount = current_tax_amount;
|
||||||
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
|
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
|
||||||
|
|
||||||
if(i==0) {
|
if(i==0) {
|
||||||
@@ -1251,9 +1245,29 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax));
|
tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax));
|
||||||
}
|
}
|
||||||
|
|
||||||
tax.base_total = tax.total * frm.doc.source_exchange_rate;
|
// tac accounts are only in company currency
|
||||||
frm.doc.total_taxes_and_charges += current_tax_amount;
|
tax.base_total = tax.total
|
||||||
frm.doc.base_total_taxes_and_charges += current_tax_amount * frm.doc.source_exchange_rate;
|
|
||||||
|
// calculate total taxes and base total taxes
|
||||||
|
if(frm.doc.payment_type == "Pay") {
|
||||||
|
// tax accounts only have company currency
|
||||||
|
if(tax.currency != frm.doc.paid_to_account_currency) {
|
||||||
|
//total_taxes_and_charges has the target currency. so using target conversion rate
|
||||||
|
frm.doc.total_taxes_and_charges += flt(current_tax_amount / frm.doc.target_exchange_rate);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
frm.doc.total_taxes_and_charges += current_tax_amount;
|
||||||
|
}
|
||||||
|
} else if(frm.doc.payment_type == "Receive") {
|
||||||
|
if(tax.currency != frm.doc.paid_from_account_currency) {
|
||||||
|
//total_taxes_and_charges has the target currency. so using source conversion rate
|
||||||
|
frm.doc.total_taxes_and_charges += flt(current_tax_amount / frm.doc.source_exchange_rate);
|
||||||
|
} else {
|
||||||
|
frm.doc.total_taxes_and_charges += current_tax_amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frm.doc.base_total_taxes_and_charges += tax.base_tax_amount;
|
||||||
|
|
||||||
frm.refresh_field('taxes');
|
frm.refresh_field('taxes');
|
||||||
frm.refresh_field('total_taxes_and_charges');
|
frm.refresh_field('total_taxes_and_charges');
|
||||||
|
|||||||
@@ -305,6 +305,7 @@
|
|||||||
"fieldname": "source_exchange_rate",
|
"fieldname": "source_exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Exchange Rate",
|
"label": "Exchange Rate",
|
||||||
|
"precision": "9",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@@ -334,6 +335,7 @@
|
|||||||
"fieldname": "target_exchange_rate",
|
"fieldname": "target_exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Exchange Rate",
|
"label": "Exchange Rate",
|
||||||
|
"precision": "9",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@@ -731,7 +733,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-02-23 20:08:39.559814",
|
"modified": "2022-12-08 16:25:43.824051",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ from erpnext.controllers.accounts_controller import (
|
|||||||
get_supplier_block_status,
|
get_supplier_block_status,
|
||||||
validate_taxes_and_charges,
|
validate_taxes_and_charges,
|
||||||
)
|
)
|
||||||
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
|
|
||||||
|
|
||||||
@@ -63,7 +62,6 @@ class PaymentEntry(AccountsController):
|
|||||||
self.set_missing_values()
|
self.set_missing_values()
|
||||||
self.validate_payment_type()
|
self.validate_payment_type()
|
||||||
self.validate_party_details()
|
self.validate_party_details()
|
||||||
self.validate_bank_accounts()
|
|
||||||
self.set_exchange_rate()
|
self.set_exchange_rate()
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
self.validate_reference_documents()
|
self.validate_reference_documents()
|
||||||
@@ -88,7 +86,6 @@ class PaymentEntry(AccountsController):
|
|||||||
if self.difference_amount:
|
if self.difference_amount:
|
||||||
frappe.throw(_("Difference Amount must be zero"))
|
frappe.throw(_("Difference Amount must be zero"))
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
self.update_expense_claim()
|
|
||||||
self.update_outstanding_amounts()
|
self.update_outstanding_amounts()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_payment_schedule()
|
self.update_payment_schedule()
|
||||||
@@ -97,7 +94,6 @@ class PaymentEntry(AccountsController):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||||
self.make_gl_entries(cancel=1)
|
self.make_gl_entries(cancel=1)
|
||||||
self.update_expense_claim()
|
|
||||||
self.update_outstanding_amounts()
|
self.update_outstanding_amounts()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.delink_advance_entry_references()
|
self.delink_advance_entry_references()
|
||||||
@@ -184,7 +180,11 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Party is mandatory"))
|
frappe.throw(_("Party is mandatory"))
|
||||||
|
|
||||||
_party_name = "title" if self.party_type == "Shareholder" else self.party_type.lower() + "_name"
|
_party_name = "title" if self.party_type == "Shareholder" else self.party_type.lower() + "_name"
|
||||||
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
|
|
||||||
|
if frappe.db.has_column(self.party_type, _party_name):
|
||||||
|
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
|
||||||
|
else:
|
||||||
|
self.party_name = frappe.db.get_value(self.party_type, self.party, "name")
|
||||||
|
|
||||||
if self.party:
|
if self.party:
|
||||||
if not self.party_balance:
|
if not self.party_balance:
|
||||||
@@ -242,29 +242,12 @@ class PaymentEntry(AccountsController):
|
|||||||
if not frappe.db.exists(self.party_type, self.party):
|
if not frappe.db.exists(self.party_type, self.party):
|
||||||
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
|
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
|
||||||
|
|
||||||
if self.party_account and self.party_type in ("Customer", "Supplier"):
|
|
||||||
self.validate_account_type(
|
|
||||||
self.party_account, [erpnext.get_party_account_type(self.party_type)]
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_bank_accounts(self):
|
|
||||||
if self.payment_type in ("Pay", "Internal Transfer"):
|
|
||||||
self.validate_account_type(self.paid_from, ["Bank", "Cash"])
|
|
||||||
|
|
||||||
if self.payment_type in ("Receive", "Internal Transfer"):
|
|
||||||
self.validate_account_type(self.paid_to, ["Bank", "Cash"])
|
|
||||||
|
|
||||||
def validate_account_type(self, account, account_types):
|
|
||||||
account_type = frappe.db.get_value("Account", account, "account_type")
|
|
||||||
# if account_type not in account_types:
|
|
||||||
# frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types)))
|
|
||||||
|
|
||||||
def set_exchange_rate(self, ref_doc=None):
|
def set_exchange_rate(self, ref_doc=None):
|
||||||
self.set_source_exchange_rate(ref_doc)
|
self.set_source_exchange_rate(ref_doc)
|
||||||
self.set_target_exchange_rate(ref_doc)
|
self.set_target_exchange_rate(ref_doc)
|
||||||
|
|
||||||
def set_source_exchange_rate(self, ref_doc=None):
|
def set_source_exchange_rate(self, ref_doc=None):
|
||||||
if self.paid_from and not self.source_exchange_rate:
|
if self.paid_from:
|
||||||
if self.paid_from_account_currency == self.company_currency:
|
if self.paid_from_account_currency == self.company_currency:
|
||||||
self.source_exchange_rate = 1
|
self.source_exchange_rate = 1
|
||||||
else:
|
else:
|
||||||
@@ -296,14 +279,10 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
|
frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
|
||||||
|
|
||||||
def validate_reference_documents(self):
|
def validate_reference_documents(self):
|
||||||
if self.party_type == "Customer":
|
valid_reference_doctypes = self.get_valid_reference_doctypes()
|
||||||
valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
|
|
||||||
elif self.party_type == "Supplier":
|
if not valid_reference_doctypes:
|
||||||
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
return
|
||||||
elif self.party_type == "Employee":
|
|
||||||
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
|
|
||||||
elif self.party_type == "Shareholder":
|
|
||||||
valid_reference_doctypes = "Journal Entry"
|
|
||||||
|
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if not d.allocated_amount:
|
if not d.allocated_amount:
|
||||||
@@ -329,7 +308,7 @@ class PaymentEntry(AccountsController):
|
|||||||
else:
|
else:
|
||||||
self.validate_journal_entry()
|
self.validate_journal_entry()
|
||||||
|
|
||||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
|
if d.reference_doctype in frappe.get_hooks("invoice_doctypes"):
|
||||||
if self.party_type == "Customer":
|
if self.party_type == "Customer":
|
||||||
ref_party_account = (
|
ref_party_account = (
|
||||||
get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
|
get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
|
||||||
@@ -355,6 +334,16 @@ class PaymentEntry(AccountsController):
|
|||||||
if ref_doc.docstatus != 1:
|
if ref_doc.docstatus != 1:
|
||||||
frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
|
frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
|
||||||
|
|
||||||
|
def get_valid_reference_doctypes(self):
|
||||||
|
if self.party_type == "Customer":
|
||||||
|
return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
|
||||||
|
elif self.party_type == "Supplier":
|
||||||
|
return ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
||||||
|
elif self.party_type == "Shareholder":
|
||||||
|
return ("Journal Entry",)
|
||||||
|
elif self.party_type == "Employee":
|
||||||
|
return ("Journal Entry",)
|
||||||
|
|
||||||
def validate_paid_invoices(self):
|
def validate_paid_invoices(self):
|
||||||
no_oustanding_refs = {}
|
no_oustanding_refs = {}
|
||||||
|
|
||||||
@@ -362,7 +351,7 @@ class PaymentEntry(AccountsController):
|
|||||||
if not d.allocated_amount:
|
if not d.allocated_amount:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
|
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
outstanding_amount, is_return = frappe.get_cached_value(
|
outstanding_amount, is_return = frappe.get_cached_value(
|
||||||
d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]
|
d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]
|
||||||
)
|
)
|
||||||
@@ -633,7 +622,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.payment_type == "Receive"
|
self.payment_type == "Receive"
|
||||||
and self.base_total_allocated_amount < self.base_received_amount + total_deductions
|
and self.base_total_allocated_amount < self.base_received_amount + total_deductions
|
||||||
and self.total_allocated_amount
|
and self.total_allocated_amount
|
||||||
< self.paid_amount + (total_deductions / self.source_exchange_rate)
|
< flt(self.paid_amount) + (total_deductions / self.source_exchange_rate)
|
||||||
):
|
):
|
||||||
self.unallocated_amount = (
|
self.unallocated_amount = (
|
||||||
self.base_received_amount + total_deductions - self.base_total_allocated_amount
|
self.base_received_amount + total_deductions - self.base_total_allocated_amount
|
||||||
@@ -643,7 +632,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.payment_type == "Pay"
|
self.payment_type == "Pay"
|
||||||
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions)
|
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions)
|
||||||
and self.total_allocated_amount
|
and self.total_allocated_amount
|
||||||
< self.received_amount + (total_deductions / self.target_exchange_rate)
|
< flt(self.received_amount) + (total_deductions / self.target_exchange_rate)
|
||||||
):
|
):
|
||||||
self.unallocated_amount = (
|
self.unallocated_amount = (
|
||||||
self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)
|
self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)
|
||||||
@@ -695,35 +684,34 @@ class PaymentEntry(AccountsController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_payment_against_negative_invoice(self):
|
def validate_payment_against_negative_invoice(self):
|
||||||
if (self.payment_type == "Pay" and self.party_type == "Customer") or (
|
if (self.payment_type != "Pay" or self.party_type != "Customer") and (
|
||||||
self.payment_type == "Receive" and self.party_type == "Supplier"
|
self.payment_type != "Receive" or self.party_type != "Supplier"
|
||||||
):
|
):
|
||||||
|
return
|
||||||
|
|
||||||
total_negative_outstanding = sum(
|
total_negative_outstanding = sum(
|
||||||
abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
|
abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
|
||||||
|
)
|
||||||
|
|
||||||
|
paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
|
||||||
|
additional_charges = sum(flt(d.amount) for d in self.deductions)
|
||||||
|
|
||||||
|
if not total_negative_outstanding:
|
||||||
|
if self.party_type == "Customer":
|
||||||
|
msg = _("Cannot pay to Customer without any negative outstanding invoice")
|
||||||
|
else:
|
||||||
|
msg = _("Cannot receive from Supplier without any negative outstanding invoice")
|
||||||
|
|
||||||
|
frappe.throw(msg, InvalidPaymentEntry)
|
||||||
|
|
||||||
|
elif paid_amount - additional_charges > total_negative_outstanding:
|
||||||
|
frappe.throw(
|
||||||
|
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
|
||||||
|
total_negative_outstanding
|
||||||
|
),
|
||||||
|
InvalidPaymentEntry,
|
||||||
)
|
)
|
||||||
|
|
||||||
paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
|
|
||||||
additional_charges = sum([flt(d.amount) for d in self.deductions])
|
|
||||||
|
|
||||||
if not total_negative_outstanding:
|
|
||||||
frappe.throw(
|
|
||||||
_("Cannot {0} {1} {2} without any negative outstanding invoice").format(
|
|
||||||
_(self.payment_type),
|
|
||||||
(_("to") if self.party_type == "Customer" else _("from")),
|
|
||||||
self.party_type,
|
|
||||||
),
|
|
||||||
InvalidPaymentEntry,
|
|
||||||
)
|
|
||||||
|
|
||||||
elif paid_amount - additional_charges > total_negative_outstanding:
|
|
||||||
frappe.throw(
|
|
||||||
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
|
|
||||||
total_negative_outstanding
|
|
||||||
),
|
|
||||||
InvalidPaymentEntry,
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_title(self):
|
def set_title(self):
|
||||||
if frappe.flags.in_import and self.title:
|
if frappe.flags.in_import and self.title:
|
||||||
# do not set title dynamically if title exists during data import.
|
# do not set title dynamically if title exists during data import.
|
||||||
@@ -736,7 +724,7 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
def validate_transaction_reference(self):
|
def validate_transaction_reference(self):
|
||||||
bank_account = self.paid_to if self.payment_type == "Receive" else self.paid_from
|
bank_account = self.paid_to if self.payment_type == "Receive" else self.paid_from
|
||||||
bank_account_type = frappe.db.get_value("Account", bank_account, "account_type")
|
bank_account_type = frappe.get_cached_value("Account", bank_account, "account_type")
|
||||||
|
|
||||||
if bank_account_type == "Bank":
|
if bank_account_type == "Bank":
|
||||||
if not self.reference_no or not self.reference_date:
|
if not self.reference_no or not self.reference_date:
|
||||||
@@ -933,6 +921,13 @@ class PaymentEntry(AccountsController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not d.included_in_paid_amount:
|
if not d.included_in_paid_amount:
|
||||||
|
if get_account_currency(payment_account) != self.company_currency:
|
||||||
|
if self.payment_type == "Receive":
|
||||||
|
exchange_rate = self.target_exchange_rate
|
||||||
|
elif self.payment_type in ["Pay", "Internal Transfer"]:
|
||||||
|
exchange_rate = self.source_exchange_rate
|
||||||
|
base_tax_amount = flt((tax_amount / exchange_rate), self.precision("paid_amount"))
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict(
|
self.get_gl_dict(
|
||||||
{
|
{
|
||||||
@@ -980,23 +975,10 @@ class PaymentEntry(AccountsController):
|
|||||||
def update_advance_paid(self):
|
def update_advance_paid(self):
|
||||||
if self.payment_type in ("Receive", "Pay") and self.party:
|
if self.payment_type in ("Receive", "Pay") and self.party:
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if d.allocated_amount and d.reference_doctype in (
|
if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
|
||||||
"Sales Order",
|
frappe.get_doc(
|
||||||
"Purchase Order",
|
d.reference_doctype, d.reference_name, for_update=True
|
||||||
"Employee Advance",
|
).set_total_advance_paid()
|
||||||
"Gratuity",
|
|
||||||
):
|
|
||||||
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
|
|
||||||
|
|
||||||
def update_expense_claim(self):
|
|
||||||
if self.payment_type in ("Pay") and self.party:
|
|
||||||
for d in self.get("references"):
|
|
||||||
if d.reference_doctype == "Expense Claim" and d.reference_name:
|
|
||||||
doc = frappe.get_doc("Expense Claim", d.reference_name)
|
|
||||||
if self.docstatus == 2:
|
|
||||||
update_reimbursed_amount(doc, -1 * d.allocated_amount)
|
|
||||||
else:
|
|
||||||
update_reimbursed_amount(doc, d.allocated_amount)
|
|
||||||
|
|
||||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||||
self.reference_no = reference_doc.name
|
self.reference_no = reference_doc.name
|
||||||
@@ -1041,7 +1023,7 @@ class PaymentEntry(AccountsController):
|
|||||||
for fieldname in tax_fields:
|
for fieldname in tax_fields:
|
||||||
tax.set(fieldname, 0.0)
|
tax.set(fieldname, 0.0)
|
||||||
|
|
||||||
self.paid_amount_after_tax = self.paid_amount
|
self.paid_amount_after_tax = self.base_paid_amount
|
||||||
|
|
||||||
def determine_exclusive_rate(self):
|
def determine_exclusive_rate(self):
|
||||||
if not any(cint(tax.included_in_paid_amount) for tax in self.get("taxes")):
|
if not any(cint(tax.included_in_paid_amount) for tax in self.get("taxes")):
|
||||||
@@ -1060,7 +1042,7 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
||||||
|
|
||||||
self.paid_amount_after_tax = flt(self.paid_amount / (1 + cumulated_tax_fraction))
|
self.paid_amount_after_tax = flt(self.base_paid_amount / (1 + cumulated_tax_fraction))
|
||||||
|
|
||||||
def calculate_taxes(self):
|
def calculate_taxes(self):
|
||||||
self.total_taxes_and_charges = 0.0
|
self.total_taxes_and_charges = 0.0
|
||||||
@@ -1083,7 +1065,7 @@ class PaymentEntry(AccountsController):
|
|||||||
current_tax_amount += actual_tax_dict[tax.idx]
|
current_tax_amount += actual_tax_dict[tax.idx]
|
||||||
|
|
||||||
tax.tax_amount = current_tax_amount
|
tax.tax_amount = current_tax_amount
|
||||||
tax.base_tax_amount = tax.tax_amount * self.source_exchange_rate
|
tax.base_tax_amount = current_tax_amount
|
||||||
|
|
||||||
if tax.add_deduct_tax == "Deduct":
|
if tax.add_deduct_tax == "Deduct":
|
||||||
current_tax_amount *= -1.0
|
current_tax_amount *= -1.0
|
||||||
@@ -1097,14 +1079,20 @@ class PaymentEntry(AccountsController):
|
|||||||
self.get("taxes")[i - 1].total + current_tax_amount, self.precision("total", tax)
|
self.get("taxes")[i - 1].total + current_tax_amount, self.precision("total", tax)
|
||||||
)
|
)
|
||||||
|
|
||||||
tax.base_total = tax.total * self.source_exchange_rate
|
tax.base_total = tax.total
|
||||||
|
|
||||||
if self.payment_type == "Pay":
|
if self.payment_type == "Pay":
|
||||||
self.base_total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
|
if tax.currency != self.paid_to_account_currency:
|
||||||
self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
|
self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
|
||||||
else:
|
else:
|
||||||
self.base_total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
|
self.total_taxes_and_charges += current_tax_amount
|
||||||
self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
|
elif self.payment_type == "Receive":
|
||||||
|
if tax.currency != self.paid_from_account_currency:
|
||||||
|
self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
|
||||||
|
else:
|
||||||
|
self.total_taxes_and_charges += current_tax_amount
|
||||||
|
|
||||||
|
self.base_total_taxes_and_charges += tax.base_tax_amount
|
||||||
|
|
||||||
if self.get("taxes"):
|
if self.get("taxes"):
|
||||||
self.paid_amount_after_tax = self.get("taxes")[-1].base_total
|
self.paid_amount_after_tax = self.get("taxes")[-1].base_total
|
||||||
@@ -1191,7 +1179,6 @@ def validate_inclusive_tax(tax, doc):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_outstanding_reference_documents(args):
|
def get_outstanding_reference_documents(args):
|
||||||
|
|
||||||
if isinstance(args, str):
|
if isinstance(args, str):
|
||||||
args = json.loads(args)
|
args = json.loads(args)
|
||||||
|
|
||||||
@@ -1200,6 +1187,8 @@ def get_outstanding_reference_documents(args):
|
|||||||
|
|
||||||
ple = qb.DocType("Payment Ledger Entry")
|
ple = qb.DocType("Payment Ledger Entry")
|
||||||
common_filter = []
|
common_filter = []
|
||||||
|
accounting_dimensions_filter = []
|
||||||
|
posting_and_due_date = []
|
||||||
|
|
||||||
# confirm that Supplier is not blocked
|
# confirm that Supplier is not blocked
|
||||||
if args.get("party_type") == "Supplier":
|
if args.get("party_type") == "Supplier":
|
||||||
@@ -1216,7 +1205,7 @@ def get_outstanding_reference_documents(args):
|
|||||||
party_account_currency = get_account_currency(args.get("party_account"))
|
party_account_currency = get_account_currency(args.get("party_account"))
|
||||||
company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency")
|
company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency")
|
||||||
|
|
||||||
# Get positive outstanding sales /purchase invoices/ Fees
|
# Get positive outstanding sales /purchase invoices
|
||||||
condition = ""
|
condition = ""
|
||||||
if args.get("voucher_type") and args.get("voucher_no"):
|
if args.get("voucher_type") and args.get("voucher_no"):
|
||||||
condition = " and voucher_type={0} and voucher_no={1}".format(
|
condition = " and voucher_type={0} and voucher_no={1}".format(
|
||||||
@@ -1228,7 +1217,7 @@ def get_outstanding_reference_documents(args):
|
|||||||
# Add cost center condition
|
# Add cost center condition
|
||||||
if args.get("cost_center"):
|
if args.get("cost_center"):
|
||||||
condition += " and cost_center='%s'" % args.get("cost_center")
|
condition += " and cost_center='%s'" % args.get("cost_center")
|
||||||
common_filter.append(ple.cost_center == args.get("cost_center"))
|
accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
|
||||||
|
|
||||||
date_fields_dict = {
|
date_fields_dict = {
|
||||||
"posting_date": ["from_posting_date", "to_posting_date"],
|
"posting_date": ["from_posting_date", "to_posting_date"],
|
||||||
@@ -1240,7 +1229,7 @@ def get_outstanding_reference_documents(args):
|
|||||||
condition += " and {0} between '{1}' and '{2}'".format(
|
condition += " and {0} between '{1}' and '{2}'".format(
|
||||||
fieldname, args.get(date_fields[0]), args.get(date_fields[1])
|
fieldname, args.get(date_fields[0]), args.get(date_fields[1])
|
||||||
)
|
)
|
||||||
common_filter.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
|
posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
|
||||||
|
|
||||||
if args.get("company"):
|
if args.get("company"):
|
||||||
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
||||||
@@ -1251,8 +1240,10 @@ def get_outstanding_reference_documents(args):
|
|||||||
args.get("party"),
|
args.get("party"),
|
||||||
args.get("party_account"),
|
args.get("party_account"),
|
||||||
common_filter=common_filter,
|
common_filter=common_filter,
|
||||||
|
posting_date=posting_and_due_date,
|
||||||
min_outstanding=args.get("outstanding_amt_greater_than"),
|
min_outstanding=args.get("outstanding_amt_greater_than"),
|
||||||
max_outstanding=args.get("outstanding_amt_less_than"),
|
max_outstanding=args.get("outstanding_amt_less_than"),
|
||||||
|
accounting_dimensions=accounting_dimensions_filter,
|
||||||
)
|
)
|
||||||
|
|
||||||
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
||||||
@@ -1260,7 +1251,7 @@ def get_outstanding_reference_documents(args):
|
|||||||
for d in outstanding_invoices:
|
for d in outstanding_invoices:
|
||||||
d["exchange_rate"] = 1
|
d["exchange_rate"] = 1
|
||||||
if party_account_currency != company_currency:
|
if party_account_currency != company_currency:
|
||||||
if d.voucher_type in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
|
if d.voucher_type in frappe.get_hooks("invoice_doctypes"):
|
||||||
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
|
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
|
||||||
elif d.voucher_type == "Journal Entry":
|
elif d.voucher_type == "Journal Entry":
|
||||||
d["exchange_rate"] = get_exchange_rate(
|
d["exchange_rate"] = get_exchange_rate(
|
||||||
@@ -1313,7 +1304,7 @@ def split_invoices_based_on_payment_terms(outstanding_invoices):
|
|||||||
d.voucher_type, d.voucher_no, "payment_terms_template"
|
d.voucher_type, d.voucher_no, "payment_terms_template"
|
||||||
)
|
)
|
||||||
if payment_term_template:
|
if payment_term_template:
|
||||||
allocate_payment_based_on_payment_terms = frappe.db.get_value(
|
allocate_payment_based_on_payment_terms = frappe.get_cached_value(
|
||||||
"Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
|
"Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
|
||||||
)
|
)
|
||||||
if allocate_payment_based_on_payment_terms:
|
if allocate_payment_based_on_payment_terms:
|
||||||
@@ -1545,7 +1536,7 @@ def get_account_details(account, date, cost_center=None):
|
|||||||
{
|
{
|
||||||
"account_currency": get_account_currency(account),
|
"account_currency": get_account_currency(account),
|
||||||
"account_balance": account_balance,
|
"account_balance": account_balance,
|
||||||
"account_type": frappe.db.get_value("Account", account, "account_type"),
|
"account_type": frappe.get_cached_value("Account", account, "account_type"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1587,20 +1578,17 @@ def get_outstanding_on_journal_entry(name):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_reference_details(reference_doctype, reference_name, party_account_currency):
|
def get_reference_details(reference_doctype, reference_name, party_account_currency):
|
||||||
total_amount = outstanding_amount = exchange_rate = bill_no = None
|
total_amount = outstanding_amount = exchange_rate = None
|
||||||
|
|
||||||
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
||||||
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(
|
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(
|
||||||
ref_doc.company
|
ref_doc.company
|
||||||
)
|
)
|
||||||
|
|
||||||
if reference_doctype == "Fees":
|
if reference_doctype == "Dunning":
|
||||||
total_amount = ref_doc.get("grand_total")
|
total_amount = outstanding_amount = ref_doc.get("dunning_amount")
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
|
||||||
elif reference_doctype == "Dunning":
|
|
||||||
total_amount = ref_doc.get("dunning_amount")
|
|
||||||
exchange_rate = 1
|
|
||||||
outstanding_amount = ref_doc.get("dunning_amount")
|
|
||||||
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
|
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
|
||||||
total_amount = ref_doc.get("total_amount")
|
total_amount = ref_doc.get("total_amount")
|
||||||
if ref_doc.multi_currency:
|
if ref_doc.multi_currency:
|
||||||
@@ -1610,48 +1598,27 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
else:
|
else:
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
||||||
|
|
||||||
elif reference_doctype != "Journal Entry":
|
elif reference_doctype != "Journal Entry":
|
||||||
if ref_doc.doctype == "Expense Claim":
|
|
||||||
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
|
||||||
elif ref_doc.doctype == "Employee Advance":
|
|
||||||
total_amount = ref_doc.advance_amount
|
|
||||||
exchange_rate = ref_doc.get("exchange_rate")
|
|
||||||
if party_account_currency != ref_doc.currency:
|
|
||||||
total_amount = flt(total_amount) * flt(exchange_rate)
|
|
||||||
elif ref_doc.doctype == "Gratuity":
|
|
||||||
total_amount = ref_doc.amount
|
|
||||||
if not total_amount:
|
if not total_amount:
|
||||||
if party_account_currency == company_currency:
|
if party_account_currency == company_currency:
|
||||||
total_amount = ref_doc.base_grand_total
|
# for handling cases that don't have multi-currency (base field)
|
||||||
|
total_amount = ref_doc.get("grand_total") or ref_doc.get("base_grand_total")
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
else:
|
else:
|
||||||
total_amount = ref_doc.grand_total
|
total_amount = ref_doc.get("grand_total")
|
||||||
if not exchange_rate:
|
if not exchange_rate:
|
||||||
# Get the exchange rate from the original ref doc
|
# Get the exchange rate from the original ref doc
|
||||||
# or get it based on the posting date of the ref doc.
|
# or get it based on the posting date of the ref doc.
|
||||||
exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
|
exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
|
||||||
party_account_currency, company_currency, ref_doc.posting_date
|
party_account_currency, company_currency, ref_doc.posting_date
|
||||||
)
|
)
|
||||||
|
|
||||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||||
bill_no = ref_doc.get("bill_no")
|
|
||||||
elif reference_doctype == "Expense Claim":
|
|
||||||
outstanding_amount = (
|
|
||||||
flt(ref_doc.get("total_sanctioned_amount"))
|
|
||||||
+ flt(ref_doc.get("total_taxes_and_charges"))
|
|
||||||
- flt(ref_doc.get("total_amount_reimbursed"))
|
|
||||||
- flt(ref_doc.get("total_advance_amount"))
|
|
||||||
)
|
|
||||||
elif reference_doctype == "Employee Advance":
|
|
||||||
outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
|
|
||||||
if party_account_currency != ref_doc.currency:
|
|
||||||
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
|
|
||||||
if party_account_currency == company_currency:
|
|
||||||
exchange_rate = 1
|
|
||||||
elif reference_doctype == "Gratuity":
|
|
||||||
outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount)
|
|
||||||
else:
|
else:
|
||||||
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
outstanding_amount = flt(total_amount) - flt(ref_doc.get("advance_paid"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Get the exchange rate based on the posting date of the ref doc.
|
# Get the exchange rate based on the posting date of the ref doc.
|
||||||
exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
||||||
@@ -1662,125 +1629,29 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
"total_amount": flt(total_amount),
|
"total_amount": flt(total_amount),
|
||||||
"outstanding_amount": flt(outstanding_amount),
|
"outstanding_amount": flt(outstanding_amount),
|
||||||
"exchange_rate": flt(exchange_rate),
|
"exchange_rate": flt(exchange_rate),
|
||||||
"bill_no": bill_no,
|
"bill_no": ref_doc.get("bill_no"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_amounts_based_on_reference_doctype(
|
|
||||||
reference_doctype, ref_doc, party_account_currency, company_currency, reference_name
|
|
||||||
):
|
|
||||||
total_amount = outstanding_amount = exchange_rate = None
|
|
||||||
if reference_doctype == "Fees":
|
|
||||||
total_amount = ref_doc.get("grand_total")
|
|
||||||
exchange_rate = 1
|
|
||||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
|
||||||
elif reference_doctype == "Dunning":
|
|
||||||
total_amount = ref_doc.get("dunning_amount")
|
|
||||||
exchange_rate = 1
|
|
||||||
outstanding_amount = ref_doc.get("dunning_amount")
|
|
||||||
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
|
|
||||||
total_amount = ref_doc.get("total_amount")
|
|
||||||
if ref_doc.multi_currency:
|
|
||||||
exchange_rate = get_exchange_rate(
|
|
||||||
party_account_currency, company_currency, ref_doc.posting_date
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exchange_rate = 1
|
|
||||||
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
|
||||||
|
|
||||||
return total_amount, outstanding_amount, exchange_rate
|
|
||||||
|
|
||||||
|
|
||||||
def get_amounts_based_on_ref_doc(
|
|
||||||
reference_doctype, ref_doc, party_account_currency, company_currency
|
|
||||||
):
|
|
||||||
total_amount = outstanding_amount = exchange_rate = None
|
|
||||||
if ref_doc.doctype == "Expense Claim":
|
|
||||||
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
|
||||||
elif ref_doc.doctype == "Employee Advance":
|
|
||||||
total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(
|
|
||||||
party_account_currency, ref_doc
|
|
||||||
)
|
|
||||||
|
|
||||||
if not total_amount:
|
|
||||||
total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
|
|
||||||
party_account_currency, company_currency, ref_doc
|
|
||||||
)
|
|
||||||
|
|
||||||
if not exchange_rate:
|
|
||||||
# Get the exchange rate from the original ref doc
|
|
||||||
# or get it based on the posting date of the ref doc
|
|
||||||
exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
|
|
||||||
party_account_currency, company_currency, ref_doc.posting_date
|
|
||||||
)
|
|
||||||
|
|
||||||
outstanding_amount, exchange_rate, bill_no = get_bill_no_and_update_amounts(
|
|
||||||
reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
|
|
||||||
)
|
|
||||||
|
|
||||||
return total_amount, outstanding_amount, exchange_rate, bill_no
|
|
||||||
|
|
||||||
|
|
||||||
def get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc):
|
|
||||||
total_amount = ref_doc.advance_amount
|
|
||||||
exchange_rate = ref_doc.get("exchange_rate")
|
|
||||||
if party_account_currency != ref_doc.currency:
|
|
||||||
total_amount = flt(total_amount) * flt(exchange_rate)
|
|
||||||
|
|
||||||
return total_amount, exchange_rate
|
|
||||||
|
|
||||||
|
|
||||||
def get_total_amount_exchange_rate_base_on_currency(
|
|
||||||
party_account_currency, company_currency, ref_doc
|
|
||||||
):
|
|
||||||
exchange_rate = None
|
|
||||||
if party_account_currency == company_currency:
|
|
||||||
total_amount = ref_doc.base_grand_total
|
|
||||||
exchange_rate = 1
|
|
||||||
else:
|
|
||||||
total_amount = ref_doc.grand_total
|
|
||||||
|
|
||||||
return total_amount, exchange_rate
|
|
||||||
|
|
||||||
|
|
||||||
def get_bill_no_and_update_amounts(
|
|
||||||
reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
|
|
||||||
):
|
|
||||||
outstanding_amount = bill_no = None
|
|
||||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
|
||||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
|
||||||
bill_no = ref_doc.get("bill_no")
|
|
||||||
elif reference_doctype == "Expense Claim":
|
|
||||||
outstanding_amount = (
|
|
||||||
flt(ref_doc.get("total_sanctioned_amount"))
|
|
||||||
+ flt(ref_doc.get("total_taxes_and_charges"))
|
|
||||||
- flt(ref_doc.get("total_amount_reimbursed"))
|
|
||||||
- flt(ref_doc.get("total_advance_amount"))
|
|
||||||
)
|
|
||||||
elif reference_doctype == "Employee Advance":
|
|
||||||
outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
|
|
||||||
if party_account_currency != ref_doc.currency:
|
|
||||||
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
|
|
||||||
if party_account_currency == company_currency:
|
|
||||||
exchange_rate = 1
|
|
||||||
else:
|
|
||||||
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
|
||||||
|
|
||||||
return outstanding_amount, exchange_rate, bill_no
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
|
def get_payment_entry(
|
||||||
|
dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None
|
||||||
|
):
|
||||||
reference_doc = None
|
reference_doc = None
|
||||||
doc = frappe.get_doc(dt, dn)
|
doc = frappe.get_doc(dt, dn)
|
||||||
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
|
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= 99.99:
|
||||||
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
|
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
|
||||||
|
|
||||||
party_type = set_party_type(dt)
|
if not party_type:
|
||||||
|
party_type = set_party_type(dt)
|
||||||
|
|
||||||
party_account = set_party_account(dt, dn, doc, party_type)
|
party_account = set_party_account(dt, dn, doc, party_type)
|
||||||
party_account_currency = set_party_account_currency(dt, party_account, doc)
|
party_account_currency = set_party_account_currency(dt, party_account, doc)
|
||||||
payment_type = set_payment_type(dt, doc)
|
|
||||||
|
if not payment_type:
|
||||||
|
payment_type = set_payment_type(dt, doc)
|
||||||
|
|
||||||
grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(
|
grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(
|
||||||
party_amount, dt, party_account_currency, doc
|
party_amount, dt, party_account_currency, doc
|
||||||
)
|
)
|
||||||
@@ -1834,9 +1705,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
||||||
frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date))
|
frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date))
|
||||||
else:
|
else:
|
||||||
if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_value(
|
if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_cached_value(
|
||||||
"Payment Terms Template",
|
"Payment Terms Template",
|
||||||
{"name": doc.payment_terms_template},
|
doc.payment_terms_template,
|
||||||
"allocate_payment_based_on_payment_terms",
|
"allocate_payment_based_on_payment_terms",
|
||||||
):
|
):
|
||||||
|
|
||||||
@@ -1887,9 +1758,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
pe.setup_party_account_field()
|
pe.setup_party_account_field()
|
||||||
pe.set_missing_values()
|
pe.set_missing_values()
|
||||||
|
|
||||||
|
update_accounting_dimensions(pe, doc)
|
||||||
|
|
||||||
if party_account and bank:
|
if party_account and bank:
|
||||||
if dt == "Employee Advance":
|
|
||||||
reference_doc = doc
|
|
||||||
pe.set_exchange_rate(ref_doc=reference_doc)
|
pe.set_exchange_rate(ref_doc=reference_doc)
|
||||||
pe.set_amounts()
|
pe.set_amounts()
|
||||||
if discount_amount:
|
if discount_amount:
|
||||||
@@ -1906,6 +1777,18 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
return pe
|
return pe
|
||||||
|
|
||||||
|
|
||||||
|
def update_accounting_dimensions(pe, doc):
|
||||||
|
"""
|
||||||
|
Updates accounting dimensions in Payment Entry based on the accounting dimensions in the reference document
|
||||||
|
"""
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||||
|
get_accounting_dimensions,
|
||||||
|
)
|
||||||
|
|
||||||
|
for dimension in get_accounting_dimensions():
|
||||||
|
pe.set(dimension, doc.get(dimension))
|
||||||
|
|
||||||
|
|
||||||
def get_bank_cash_account(doc, bank_account):
|
def get_bank_cash_account(doc, bank_account):
|
||||||
bank = get_default_bank_cash_account(
|
bank = get_default_bank_cash_account(
|
||||||
doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
|
doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
|
||||||
@@ -1924,8 +1807,6 @@ def set_party_type(dt):
|
|||||||
party_type = "Customer"
|
party_type = "Customer"
|
||||||
elif dt in ("Purchase Invoice", "Purchase Order"):
|
elif dt in ("Purchase Invoice", "Purchase Order"):
|
||||||
party_type = "Supplier"
|
party_type = "Supplier"
|
||||||
elif dt in ("Expense Claim", "Employee Advance", "Gratuity"):
|
|
||||||
party_type = "Employee"
|
|
||||||
return party_type
|
return party_type
|
||||||
|
|
||||||
|
|
||||||
@@ -1934,14 +1815,6 @@ def set_party_account(dt, dn, doc, party_type):
|
|||||||
party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
|
party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
|
||||||
elif dt == "Purchase Invoice":
|
elif dt == "Purchase Invoice":
|
||||||
party_account = doc.credit_to
|
party_account = doc.credit_to
|
||||||
elif dt == "Fees":
|
|
||||||
party_account = doc.receivable_account
|
|
||||||
elif dt == "Employee Advance":
|
|
||||||
party_account = doc.advance_account
|
|
||||||
elif dt == "Expense Claim":
|
|
||||||
party_account = doc.payable_account
|
|
||||||
elif dt == "Gratuity":
|
|
||||||
party_account = doc.payable_account
|
|
||||||
else:
|
else:
|
||||||
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
||||||
return party_account
|
return party_account
|
||||||
@@ -1957,8 +1830,7 @@ def set_party_account_currency(dt, party_account, doc):
|
|||||||
|
|
||||||
def set_payment_type(dt, doc):
|
def set_payment_type(dt, doc):
|
||||||
if (
|
if (
|
||||||
dt == "Sales Order"
|
dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0)
|
||||||
or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)
|
|
||||||
) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
|
) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
|
||||||
payment_type = "Receive"
|
payment_type = "Receive"
|
||||||
else:
|
else:
|
||||||
@@ -1976,30 +1848,15 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre
|
|||||||
else:
|
else:
|
||||||
grand_total = doc.rounded_total or doc.grand_total
|
grand_total = doc.rounded_total or doc.grand_total
|
||||||
outstanding_amount = doc.outstanding_amount
|
outstanding_amount = doc.outstanding_amount
|
||||||
elif dt in ("Expense Claim"):
|
|
||||||
grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges
|
|
||||||
outstanding_amount = doc.grand_total - doc.total_amount_reimbursed
|
|
||||||
elif dt == "Employee Advance":
|
|
||||||
grand_total = flt(doc.advance_amount)
|
|
||||||
outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
|
|
||||||
if party_account_currency != doc.currency:
|
|
||||||
grand_total = flt(doc.advance_amount) * flt(doc.exchange_rate)
|
|
||||||
outstanding_amount = (flt(doc.advance_amount) - flt(doc.paid_amount)) * flt(doc.exchange_rate)
|
|
||||||
elif dt == "Fees":
|
|
||||||
grand_total = doc.grand_total
|
|
||||||
outstanding_amount = doc.outstanding_amount
|
|
||||||
elif dt == "Dunning":
|
elif dt == "Dunning":
|
||||||
grand_total = doc.grand_total
|
grand_total = doc.grand_total
|
||||||
outstanding_amount = doc.grand_total
|
outstanding_amount = doc.grand_total
|
||||||
elif dt == "Gratuity":
|
|
||||||
grand_total = doc.amount
|
|
||||||
outstanding_amount = flt(doc.amount) - flt(doc.paid_amount)
|
|
||||||
else:
|
else:
|
||||||
if party_account_currency == doc.company_currency:
|
if party_account_currency == doc.company_currency:
|
||||||
grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
|
grand_total = flt(doc.get("base_rounded_total") or doc.get("base_grand_total"))
|
||||||
else:
|
else:
|
||||||
grand_total = flt(doc.get("rounded_total") or doc.grand_total)
|
grand_total = flt(doc.get("rounded_total") or doc.get("grand_total"))
|
||||||
outstanding_amount = grand_total - flt(doc.advance_paid)
|
outstanding_amount = doc.get("outstanding_amount") or (grand_total - flt(doc.advance_paid))
|
||||||
return grand_total, outstanding_amount
|
return grand_total, outstanding_amount
|
||||||
|
|
||||||
|
|
||||||
@@ -2015,8 +1872,6 @@ def set_paid_amount_and_received_amount(
|
|||||||
received_amount = bank_amount
|
received_amount = bank_amount
|
||||||
else:
|
else:
|
||||||
received_amount = paid_amount * doc.get("conversion_rate", 1)
|
received_amount = paid_amount * doc.get("conversion_rate", 1)
|
||||||
if dt == "Employee Advance":
|
|
||||||
received_amount = paid_amount * doc.get("exchange_rate", 1)
|
|
||||||
else:
|
else:
|
||||||
received_amount = abs(outstanding_amount)
|
received_amount = abs(outstanding_amount)
|
||||||
if bank_amount:
|
if bank_amount:
|
||||||
@@ -2024,8 +1879,6 @@ def set_paid_amount_and_received_amount(
|
|||||||
else:
|
else:
|
||||||
# if party account currency and bank currency is different then populate paid amount as well
|
# if party account currency and bank currency is different then populate paid amount as well
|
||||||
paid_amount = received_amount * doc.get("conversion_rate", 1)
|
paid_amount = received_amount * doc.get("conversion_rate", 1)
|
||||||
if dt == "Employee Advance":
|
|
||||||
paid_amount = received_amount * doc.get("exchange_rate", 1)
|
|
||||||
|
|
||||||
return paid_amount, received_amount
|
return paid_amount, received_amount
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
frappe.ui.form.on("Payment Entry", {
|
|
||||||
company: function(frm) {
|
|
||||||
frappe.call({
|
|
||||||
'method': 'frappe.contacts.doctype.address.address.get_default_address',
|
|
||||||
'args': {
|
|
||||||
'doctype': 'Company',
|
|
||||||
'name': frm.doc.company
|
|
||||||
},
|
|
||||||
'callback': function(r) {
|
|
||||||
frm.set_value('company_address', r.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
party: function(frm) {
|
|
||||||
if (frm.doc.party_type == "Customer" && frm.doc.party) {
|
|
||||||
frappe.call({
|
|
||||||
'method': 'frappe.contacts.doctype.address.address.get_default_address',
|
|
||||||
'args': {
|
|
||||||
'doctype': 'Customer',
|
|
||||||
'name': frm.doc.party
|
|
||||||
},
|
|
||||||
'callback': function(r) {
|
|
||||||
frm.set_value('customer_address', r.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import qb
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
from frappe.utils import flt, nowdate
|
from frappe.utils import flt, nowdate
|
||||||
|
|
||||||
@@ -19,8 +20,8 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
|
|||||||
create_sales_invoice,
|
create_sales_invoice,
|
||||||
create_sales_invoice_against_cost_center,
|
create_sales_invoice_against_cost_center,
|
||||||
)
|
)
|
||||||
from erpnext.hr.doctype.expense_claim.test_expense_claim import make_expense_claim
|
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
|
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
test_dependencies = ["Item"]
|
test_dependencies = ["Item"]
|
||||||
|
|
||||||
@@ -297,31 +298,6 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
self.assertEqual(flt(outstanding_amount), 250)
|
self.assertEqual(flt(outstanding_amount), 250)
|
||||||
self.assertEqual(status, "Unpaid")
|
self.assertEqual(status, "Unpaid")
|
||||||
|
|
||||||
def test_payment_entry_against_ec(self):
|
|
||||||
|
|
||||||
payable = frappe.get_cached_value("Company", "_Test Company", "default_payable_account")
|
|
||||||
ec = make_expense_claim(payable, 300, 300, "_Test Company", "Travel Expenses - _TC")
|
|
||||||
pe = get_payment_entry(
|
|
||||||
"Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300
|
|
||||||
)
|
|
||||||
pe.reference_no = "1"
|
|
||||||
pe.reference_date = "2016-01-01"
|
|
||||||
pe.source_exchange_rate = 1
|
|
||||||
pe.paid_to = payable
|
|
||||||
pe.insert()
|
|
||||||
pe.submit()
|
|
||||||
|
|
||||||
expected_gle = dict(
|
|
||||||
(d[0], d) for d in [[payable, 300, 0, ec.name], ["_Test Bank USD - _TC", 0, 300, None]]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.validate_gl_entries(pe.name, expected_gle)
|
|
||||||
|
|
||||||
outstanding_amount = flt(
|
|
||||||
frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")
|
|
||||||
) - flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
|
|
||||||
self.assertEqual(outstanding_amount, 0)
|
|
||||||
|
|
||||||
def test_payment_entry_against_si_usd_to_inr(self):
|
def test_payment_entry_against_si_usd_to_inr(self):
|
||||||
si = create_sales_invoice(
|
si = create_sales_invoice(
|
||||||
customer="_Test Customer USD",
|
customer="_Test Customer USD",
|
||||||
@@ -747,6 +723,46 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2)
|
flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_gl_of_multi_currency_payment_with_taxes(self):
|
||||||
|
payment_entry = create_payment_entry(
|
||||||
|
party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True
|
||||||
|
)
|
||||||
|
payment_entry.append(
|
||||||
|
"taxes",
|
||||||
|
{
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"charge_type": "Actual",
|
||||||
|
"tax_amount": 100,
|
||||||
|
"add_deduct_tax": "Add",
|
||||||
|
"description": "Test",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
payment_entry.target_exchange_rate = 80
|
||||||
|
payment_entry.received_amount = 12.5
|
||||||
|
payment_entry = payment_entry.submit()
|
||||||
|
gle = qb.DocType("GL Entry")
|
||||||
|
gl_entries = (
|
||||||
|
qb.from_(gle)
|
||||||
|
.select(
|
||||||
|
gle.account,
|
||||||
|
gle.debit,
|
||||||
|
gle.credit,
|
||||||
|
gle.debit_in_account_currency,
|
||||||
|
gle.credit_in_account_currency,
|
||||||
|
)
|
||||||
|
.orderby(gle.account)
|
||||||
|
.where(gle.voucher_no == payment_entry.name)
|
||||||
|
.run()
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_gl_entries = (
|
||||||
|
("_Test Account Service Tax - _TC", 100.0, 0.0, 100.0, 0.0),
|
||||||
|
("_Test Bank - _TC", 0.0, 1100.0, 0.0, 1100.0),
|
||||||
|
("_Test Payable USD - _TC", 1000.0, 0.0, 12.5, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(gl_entries, expected_gl_entries)
|
||||||
|
|
||||||
def test_payment_entry_against_onhold_purchase_invoice(self):
|
def test_payment_entry_against_onhold_purchase_invoice(self):
|
||||||
pi = make_purchase_invoice()
|
pi = make_purchase_invoice()
|
||||||
|
|
||||||
@@ -762,6 +778,10 @@ class TestPaymentEntry(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertTrue("is on hold" in str(err.exception).lower())
|
self.assertTrue("is on hold" in str(err.exception).lower())
|
||||||
|
|
||||||
|
def test_payment_entry_for_employee(self):
|
||||||
|
employee = make_employee("test_payment_entry@salary.com", company="_Test Company")
|
||||||
|
create_payment_entry(party_type="Employee", party=employee, save=True)
|
||||||
|
|
||||||
|
|
||||||
def create_payment_entry(**args):
|
def create_payment_entry(**args):
|
||||||
payment_entry = frappe.new_doc("Payment Entry")
|
payment_entry = frappe.new_doc("Payment Entry")
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"options": "DocType",
|
"options": "DocType",
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
@@ -35,7 +36,8 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"options": "reference_doctype",
|
"options": "reference_doctype",
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "due_date",
|
"fieldname": "due_date",
|
||||||
@@ -104,7 +106,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-09-26 17:06:55.597389",
|
"modified": "2022-12-12 12:31:44.919895",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Reference",
|
"name": "Payment Entry Reference",
|
||||||
@@ -113,5 +115,6 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Payment Gateway Account', {
|
frappe.ui.form.on('Payment Gateway Account', {
|
||||||
refresh(frm) {
|
refresh(frm) {
|
||||||
|
erpnext.utils.check_payments_app();
|
||||||
if(!frm.doc.__islocal) {
|
if(!frm.doc.__islocal) {
|
||||||
frm.set_df_property('payment_gateway', 'read_only', 1);
|
frm.set_df_property('payment_gateway', 'read_only', 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class PaymentGatewayAccount(Document):
|
|||||||
self.name = self.payment_gateway + " - " + self.currency
|
self.name = self.payment_gateway + " - " + self.currency
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.currency = frappe.db.get_value("Account", self.payment_account, "account_currency")
|
self.currency = frappe.get_cached_value("Account", self.payment_account, "account_currency")
|
||||||
|
|
||||||
self.update_default_payment_gateway()
|
self.update_default_payment_gateway()
|
||||||
self.set_as_default_if_not_set()
|
self.set_as_default_if_not_set()
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
"amount",
|
"amount",
|
||||||
"account_currency",
|
"account_currency",
|
||||||
"amount_in_account_currency",
|
"amount_in_account_currency",
|
||||||
"delinked"
|
"delinked",
|
||||||
|
"remarks"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -40,19 +41,22 @@
|
|||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Account",
|
"label": "Account",
|
||||||
"options": "Account"
|
"options": "Account",
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "party_type",
|
"fieldname": "party_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Party Type",
|
"label": "Party Type",
|
||||||
"options": "DocType"
|
"options": "DocType",
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "party",
|
"fieldname": "party",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"label": "Party",
|
"label": "Party",
|
||||||
"options": "party_type"
|
"options": "party_type",
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "voucher_type",
|
"fieldname": "voucher_type",
|
||||||
@@ -114,7 +118,8 @@
|
|||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"options": "Company"
|
"options": "Company",
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "cost_center",
|
"fieldname": "cost_center",
|
||||||
@@ -132,12 +137,17 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Finance Book",
|
"label": "Finance Book",
|
||||||
"options": "Finance Book"
|
"options": "Finance Book"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "remarks",
|
||||||
|
"fieldtype": "Text",
|
||||||
|
"label": "Remarks"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-05-30 19:04:55.532171",
|
"modified": "2022-08-22 15:32:56.629430",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Ledger Entry",
|
"name": "Payment Ledger Entry",
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class PaymentLedgerEntry(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_dimensions_for_pl_and_bs(self):
|
def validate_dimensions_for_pl_and_bs(self):
|
||||||
account_type = frappe.db.get_value("Account", self.account, "report_type")
|
account_type = frappe.get_cached_value("Account", self.account, "report_type")
|
||||||
|
|
||||||
for dimension in get_checks_for_pl_and_bs_accounts():
|
for dimension in get_checks_for_pl_and_bs_accounts():
|
||||||
if (
|
if (
|
||||||
@@ -147,3 +147,8 @@ class PaymentLedgerEntry(Document):
|
|||||||
update_voucher_outstanding(
|
update_voucher_outstanding(
|
||||||
self.against_voucher_type, self.against_voucher_no, self.account, self.party_type, self.party
|
self.against_voucher_type, self.against_voucher_no, self.account, self.party_type, self.party
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def on_doctype_update():
|
||||||
|
frappe.db.add_index("Payment Ledger Entry", ["against_voucher_no", "against_voucher_type"])
|
||||||
|
frappe.db.add_index("Payment Ledger Entry", ["voucher_no", "voucher_type"])
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import qb
|
from frappe import qb
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
|
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
|
|
||||||
|
|
||||||
@@ -127,6 +128,25 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
|||||||
payment.posting_date = posting_date
|
payment.posting_date = posting_date
|
||||||
return payment
|
return payment
|
||||||
|
|
||||||
|
def create_sales_order(
|
||||||
|
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||||
|
):
|
||||||
|
so = make_sales_order(
|
||||||
|
company=self.company,
|
||||||
|
transaction_date=posting_date,
|
||||||
|
customer=self.customer,
|
||||||
|
item_code=self.item,
|
||||||
|
cost_center=self.cost_center,
|
||||||
|
warehouse=self.warehouse,
|
||||||
|
debit_to=self.debit_to,
|
||||||
|
currency="INR",
|
||||||
|
qty=qty,
|
||||||
|
rate=100,
|
||||||
|
do_not_save=do_not_save,
|
||||||
|
do_not_submit=do_not_submit,
|
||||||
|
)
|
||||||
|
return so
|
||||||
|
|
||||||
def clear_old_entries(self):
|
def clear_old_entries(self):
|
||||||
doctype_list = [
|
doctype_list = [
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
@@ -406,3 +426,89 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
|||||||
]
|
]
|
||||||
self.assertEqual(pl_entries_for_crnote[0], expected_values[0])
|
self.assertEqual(pl_entries_for_crnote[0], expected_values[0])
|
||||||
self.assertEqual(pl_entries_for_crnote[1], expected_values[1])
|
self.assertEqual(pl_entries_for_crnote[1], expected_values[1])
|
||||||
|
|
||||||
|
@change_settings(
|
||||||
|
"Accounts Settings",
|
||||||
|
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
|
||||||
|
)
|
||||||
|
def test_multi_payment_unlink_on_invoice_cancellation(self):
|
||||||
|
transaction_date = nowdate()
|
||||||
|
amount = 100
|
||||||
|
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
|
||||||
|
|
||||||
|
for amt in [40, 40, 20]:
|
||||||
|
# payment 1
|
||||||
|
pe = get_payment_entry(si.doctype, si.name)
|
||||||
|
pe.paid_amount = amt
|
||||||
|
pe.get("references")[0].allocated_amount = amt
|
||||||
|
pe = pe.save().submit()
|
||||||
|
|
||||||
|
si.reload()
|
||||||
|
si.cancel()
|
||||||
|
|
||||||
|
entries = frappe.db.get_list(
|
||||||
|
"Payment Ledger Entry",
|
||||||
|
filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
|
||||||
|
)
|
||||||
|
self.assertEqual(entries, [])
|
||||||
|
|
||||||
|
# with references removed, deletion should be possible
|
||||||
|
si.delete()
|
||||||
|
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
|
||||||
|
|
||||||
|
@change_settings(
|
||||||
|
"Accounts Settings",
|
||||||
|
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
|
||||||
|
)
|
||||||
|
def test_multi_je_unlink_on_invoice_cancellation(self):
|
||||||
|
transaction_date = nowdate()
|
||||||
|
amount = 100
|
||||||
|
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
|
||||||
|
|
||||||
|
# multiple JE's against invoice
|
||||||
|
for amt in [40, 40, 20]:
|
||||||
|
je1 = self.create_journal_entry(
|
||||||
|
self.income_account, self.debit_to, amt, posting_date=transaction_date
|
||||||
|
)
|
||||||
|
je1.get("accounts")[1].party_type = "Customer"
|
||||||
|
je1.get("accounts")[1].party = self.customer
|
||||||
|
je1.get("accounts")[1].reference_type = si.doctype
|
||||||
|
je1.get("accounts")[1].reference_name = si.name
|
||||||
|
je1 = je1.save().submit()
|
||||||
|
|
||||||
|
si.reload()
|
||||||
|
si.cancel()
|
||||||
|
|
||||||
|
entries = frappe.db.get_list(
|
||||||
|
"Payment Ledger Entry",
|
||||||
|
filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
|
||||||
|
)
|
||||||
|
self.assertEqual(entries, [])
|
||||||
|
|
||||||
|
# with references removed, deletion should be possible
|
||||||
|
si.delete()
|
||||||
|
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
|
||||||
|
|
||||||
|
@change_settings(
|
||||||
|
"Accounts Settings",
|
||||||
|
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
|
||||||
|
)
|
||||||
|
def test_advance_payment_unlink_on_order_cancellation(self):
|
||||||
|
transaction_date = nowdate()
|
||||||
|
amount = 100
|
||||||
|
so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit()
|
||||||
|
|
||||||
|
pe = get_payment_entry(so.doctype, so.name).save().submit()
|
||||||
|
|
||||||
|
so.reload()
|
||||||
|
so.cancel()
|
||||||
|
|
||||||
|
entries = frappe.db.get_list(
|
||||||
|
"Payment Ledger Entry",
|
||||||
|
filters={"against_voucher_type": so.doctype, "against_voucher_no": so.name, "delinked": 0},
|
||||||
|
)
|
||||||
|
self.assertEqual(entries, [])
|
||||||
|
|
||||||
|
# with references removed, deletion should be possible
|
||||||
|
so.delete()
|
||||||
|
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, so.doctype, so.name)
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
reconcile() {
|
reconcile() {
|
||||||
var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
|
var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount);
|
||||||
|
|
||||||
if (show_dialog && show_dialog.length) {
|
if (show_dialog && show_dialog.length) {
|
||||||
|
|
||||||
@@ -179,8 +179,12 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
|
|||||||
title: __("Select Difference Account"),
|
title: __("Select Difference Account"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
fieldname: "allocation", fieldtype: "Table", label: __("Allocation"),
|
fieldname: "allocation",
|
||||||
data: this.data, in_place_edit: true,
|
fieldtype: "Table",
|
||||||
|
label: __("Allocation"),
|
||||||
|
data: this.data,
|
||||||
|
in_place_edit: true,
|
||||||
|
cannot_add_rows: true,
|
||||||
get_data: () => {
|
get_data: () => {
|
||||||
return this.data;
|
return this.data;
|
||||||
},
|
},
|
||||||
@@ -218,6 +222,10 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
|
|||||||
read_only: 1
|
read_only: 1
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'HTML',
|
||||||
|
options: "<b> New Journal Entry will be posted for the difference amount </b>"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
primary_action: () => {
|
primary_action: () => {
|
||||||
const args = dialog.get_values()["allocation"];
|
const args = dialog.get_values()["allocation"];
|
||||||
@@ -234,7 +242,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.frm.doc.allocation.forEach(d => {
|
this.frm.doc.allocation.forEach(d => {
|
||||||
if (d.difference_amount && !d.difference_account) {
|
if (d.difference_amount) {
|
||||||
dialog.fields_dict.allocation.df.data.push({
|
dialog.fields_dict.allocation.df.data.push({
|
||||||
'docname': d.name,
|
'docname': d.name,
|
||||||
'reference_name': d.reference_name,
|
'reference_name': d.reference_name,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class PaymentReconciliation(Document):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PaymentReconciliation, self).__init__(*args, **kwargs)
|
super(PaymentReconciliation, self).__init__(*args, **kwargs)
|
||||||
self.common_filter_conditions = []
|
self.common_filter_conditions = []
|
||||||
|
self.accounting_dimension_filter_conditions = []
|
||||||
|
self.ple_posting_date_filter = []
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_unreconciled_entries(self):
|
def get_unreconciled_entries(self):
|
||||||
@@ -67,6 +69,10 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
def get_jv_entries(self):
|
def get_jv_entries(self):
|
||||||
condition = self.get_conditions()
|
condition = self.get_conditions()
|
||||||
|
|
||||||
|
if self.get("cost_center"):
|
||||||
|
condition += f" and t2.cost_center = '{self.cost_center}' "
|
||||||
|
|
||||||
dr_or_cr = (
|
dr_or_cr = (
|
||||||
"credit_in_account_currency"
|
"credit_in_account_currency"
|
||||||
if erpnext.get_party_account_type(self.party_type) == "Receivable"
|
if erpnext.get_party_account_type(self.party_type) == "Receivable"
|
||||||
@@ -77,12 +83,13 @@ class PaymentReconciliation(Document):
|
|||||||
"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
|
"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# nosemgrep
|
||||||
journal_entries = frappe.db.sql(
|
journal_entries = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select
|
select
|
||||||
"Journal Entry" as reference_type, t1.name as reference_name,
|
"Journal Entry" as reference_type, t1.name as reference_name,
|
||||||
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
|
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
|
||||||
{dr_or_cr} as amount, t2.is_advance,
|
{dr_or_cr} as amount, t2.is_advance, t2.exchange_rate,
|
||||||
t2.account_currency as currency
|
t2.account_currency as currency
|
||||||
from
|
from
|
||||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||||
@@ -150,6 +157,7 @@ class PaymentReconciliation(Document):
|
|||||||
return_outstanding = ple_query.get_voucher_outstandings(
|
return_outstanding = ple_query.get_voucher_outstandings(
|
||||||
vouchers=return_invoices,
|
vouchers=return_invoices,
|
||||||
common_filter=self.common_filter_conditions,
|
common_filter=self.common_filter_conditions,
|
||||||
|
posting_date=self.ple_posting_date_filter,
|
||||||
min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
|
min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
|
||||||
max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
|
max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
|
||||||
get_payments=True,
|
get_payments=True,
|
||||||
@@ -162,7 +170,7 @@ class PaymentReconciliation(Document):
|
|||||||
{
|
{
|
||||||
"reference_type": inv.voucher_type,
|
"reference_type": inv.voucher_type,
|
||||||
"reference_name": inv.voucher_no,
|
"reference_name": inv.voucher_no,
|
||||||
"amount": -(inv.outstanding),
|
"amount": -(inv.outstanding_in_account_currency),
|
||||||
"posting_date": inv.posting_date,
|
"posting_date": inv.posting_date,
|
||||||
"currency": inv.currency,
|
"currency": inv.currency,
|
||||||
}
|
}
|
||||||
@@ -187,8 +195,10 @@ class PaymentReconciliation(Document):
|
|||||||
self.party,
|
self.party,
|
||||||
self.receivable_payable_account,
|
self.receivable_payable_account,
|
||||||
common_filter=self.common_filter_conditions,
|
common_filter=self.common_filter_conditions,
|
||||||
|
posting_date=self.ple_posting_date_filter,
|
||||||
min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None,
|
min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None,
|
||||||
max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None,
|
max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None,
|
||||||
|
accounting_dimensions=self.accounting_dimension_filter_conditions,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.invoice_limit:
|
if self.invoice_limit:
|
||||||
@@ -209,9 +219,26 @@ class PaymentReconciliation(Document):
|
|||||||
inv.currency = entry.get("currency")
|
inv.currency = entry.get("currency")
|
||||||
inv.outstanding_amount = flt(entry.get("outstanding_amount"))
|
inv.outstanding_amount = flt(entry.get("outstanding_amount"))
|
||||||
|
|
||||||
|
def get_difference_amount(self, payment_entry, invoice, allocated_amount):
|
||||||
|
difference_amount = 0
|
||||||
|
if invoice.get("exchange_rate") and payment_entry.get("exchange_rate", 1) != invoice.get(
|
||||||
|
"exchange_rate", 1
|
||||||
|
):
|
||||||
|
allocated_amount_in_ref_rate = payment_entry.get("exchange_rate", 1) * allocated_amount
|
||||||
|
allocated_amount_in_inv_rate = invoice.get("exchange_rate", 1) * allocated_amount
|
||||||
|
difference_amount = allocated_amount_in_ref_rate - allocated_amount_in_inv_rate
|
||||||
|
|
||||||
|
return difference_amount
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def allocate_entries(self, args):
|
def allocate_entries(self, args):
|
||||||
self.validate_entries()
|
self.validate_entries()
|
||||||
|
|
||||||
|
invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"))
|
||||||
|
default_exchange_gain_loss_account = frappe.get_cached_value(
|
||||||
|
"Company", self.company, "exchange_gain_loss_account"
|
||||||
|
)
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
for pay in args.get("payments"):
|
for pay in args.get("payments"):
|
||||||
pay.update({"unreconciled_amount": pay.get("amount")})
|
pay.update({"unreconciled_amount": pay.get("amount")})
|
||||||
@@ -224,12 +251,19 @@ class PaymentReconciliation(Document):
|
|||||||
res = self.get_allocated_entry(pay, inv, pay["amount"])
|
res = self.get_allocated_entry(pay, inv, pay["amount"])
|
||||||
inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount"))
|
inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount"))
|
||||||
pay["amount"] = 0
|
pay["amount"] = 0
|
||||||
|
|
||||||
|
inv["exchange_rate"] = invoice_exchange_map.get(inv.get("invoice_number"))
|
||||||
|
res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"])
|
||||||
|
res.difference_account = default_exchange_gain_loss_account
|
||||||
|
res.exchange_rate = inv.get("exchange_rate")
|
||||||
|
|
||||||
if pay.get("amount") == 0:
|
if pay.get("amount") == 0:
|
||||||
entries.append(res)
|
entries.append(res)
|
||||||
break
|
break
|
||||||
elif inv.get("outstanding_amount") == 0:
|
elif inv.get("outstanding_amount") == 0:
|
||||||
entries.append(res)
|
entries.append(res)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -251,6 +285,7 @@ class PaymentReconciliation(Document):
|
|||||||
"amount": pay.get("amount"),
|
"amount": pay.get("amount"),
|
||||||
"allocated_amount": allocated_amount,
|
"allocated_amount": allocated_amount,
|
||||||
"difference_amount": pay.get("difference_amount"),
|
"difference_amount": pay.get("difference_amount"),
|
||||||
|
"currency": inv.get("currency"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -273,7 +308,11 @@ class PaymentReconciliation(Document):
|
|||||||
else:
|
else:
|
||||||
reconciled_entry = entry_list
|
reconciled_entry = entry_list
|
||||||
|
|
||||||
reconciled_entry.append(self.get_payment_details(row, dr_or_cr))
|
payment_details = self.get_payment_details(row, dr_or_cr)
|
||||||
|
reconciled_entry.append(payment_details)
|
||||||
|
|
||||||
|
if payment_details.difference_amount:
|
||||||
|
self.make_difference_entry(payment_details)
|
||||||
|
|
||||||
if entry_list:
|
if entry_list:
|
||||||
reconcile_against_document(entry_list)
|
reconcile_against_document(entry_list)
|
||||||
@@ -284,6 +323,56 @@ class PaymentReconciliation(Document):
|
|||||||
msgprint(_("Successfully Reconciled"))
|
msgprint(_("Successfully Reconciled"))
|
||||||
self.get_unreconciled_entries()
|
self.get_unreconciled_entries()
|
||||||
|
|
||||||
|
def make_difference_entry(self, row):
|
||||||
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
|
journal_entry.voucher_type = "Exchange Gain Or Loss"
|
||||||
|
journal_entry.company = self.company
|
||||||
|
journal_entry.posting_date = nowdate()
|
||||||
|
journal_entry.multi_currency = 1
|
||||||
|
|
||||||
|
party_account_currency = frappe.get_cached_value(
|
||||||
|
"Account", self.receivable_payable_account, "account_currency"
|
||||||
|
)
|
||||||
|
difference_account_currency = frappe.get_cached_value(
|
||||||
|
"Account", row.difference_account, "account_currency"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Account Currency has balance
|
||||||
|
dr_or_cr = "debit" if self.party_type == "Customer" else "credit"
|
||||||
|
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
||||||
|
|
||||||
|
journal_account = frappe._dict(
|
||||||
|
{
|
||||||
|
"account": self.receivable_payable_account,
|
||||||
|
"party_type": self.party_type,
|
||||||
|
"party": self.party,
|
||||||
|
"account_currency": party_account_currency,
|
||||||
|
"exchange_rate": 0,
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
|
"reference_type": row.against_voucher_type,
|
||||||
|
"reference_name": row.against_voucher,
|
||||||
|
dr_or_cr: flt(row.difference_amount),
|
||||||
|
dr_or_cr + "_in_account_currency": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
journal_entry.append("accounts", journal_account)
|
||||||
|
|
||||||
|
journal_account = frappe._dict(
|
||||||
|
{
|
||||||
|
"account": row.difference_account,
|
||||||
|
"account_currency": difference_account_currency,
|
||||||
|
"exchange_rate": 1,
|
||||||
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
|
reverse_dr_or_cr + "_in_account_currency": flt(row.difference_amount),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
journal_entry.append("accounts", journal_account)
|
||||||
|
|
||||||
|
journal_entry.save()
|
||||||
|
journal_entry.submit()
|
||||||
|
|
||||||
def get_payment_details(self, row, dr_or_cr):
|
def get_payment_details(self, row, dr_or_cr):
|
||||||
return frappe._dict(
|
return frappe._dict(
|
||||||
{
|
{
|
||||||
@@ -293,6 +382,7 @@ class PaymentReconciliation(Document):
|
|||||||
"against_voucher_type": row.get("invoice_type"),
|
"against_voucher_type": row.get("invoice_type"),
|
||||||
"against_voucher": row.get("invoice_number"),
|
"against_voucher": row.get("invoice_number"),
|
||||||
"account": self.receivable_payable_account,
|
"account": self.receivable_payable_account,
|
||||||
|
"exchange_rate": row.get("exchange_rate"),
|
||||||
"party_type": self.party_type,
|
"party_type": self.party_type,
|
||||||
"party": self.party,
|
"party": self.party,
|
||||||
"is_advance": row.get("is_advance"),
|
"is_advance": row.get("is_advance"),
|
||||||
@@ -317,6 +407,41 @@ class PaymentReconciliation(Document):
|
|||||||
if not self.get("payments"):
|
if not self.get("payments"):
|
||||||
frappe.throw(_("No records found in the Payments table"))
|
frappe.throw(_("No records found in the Payments table"))
|
||||||
|
|
||||||
|
def get_invoice_exchange_map(self, invoices):
|
||||||
|
sales_invoices = [
|
||||||
|
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Sales Invoice"
|
||||||
|
]
|
||||||
|
purchase_invoices = [
|
||||||
|
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Purchase Invoice"
|
||||||
|
]
|
||||||
|
invoice_exchange_map = frappe._dict()
|
||||||
|
|
||||||
|
if sales_invoices:
|
||||||
|
sales_invoice_map = frappe._dict(
|
||||||
|
frappe.db.get_all(
|
||||||
|
"Sales Invoice",
|
||||||
|
filters={"name": ("in", sales_invoices)},
|
||||||
|
fields=["name", "conversion_rate"],
|
||||||
|
as_list=1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
invoice_exchange_map.update(sales_invoice_map)
|
||||||
|
|
||||||
|
if purchase_invoices:
|
||||||
|
purchase_invoice_map = frappe._dict(
|
||||||
|
frappe.db.get_all(
|
||||||
|
"Purchase Invoice",
|
||||||
|
filters={"name": ("in", purchase_invoices)},
|
||||||
|
fields=["name", "conversion_rate"],
|
||||||
|
as_list=1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
invoice_exchange_map.update(purchase_invoice_map)
|
||||||
|
|
||||||
|
return invoice_exchange_map
|
||||||
|
|
||||||
def validate_allocation(self):
|
def validate_allocation(self):
|
||||||
unreconciled_invoices = frappe._dict()
|
unreconciled_invoices = frappe._dict()
|
||||||
|
|
||||||
@@ -350,24 +475,26 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
|
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
|
||||||
self.common_filter_conditions.clear()
|
self.common_filter_conditions.clear()
|
||||||
|
self.accounting_dimension_filter_conditions.clear()
|
||||||
|
self.ple_posting_date_filter.clear()
|
||||||
ple = qb.DocType("Payment Ledger Entry")
|
ple = qb.DocType("Payment Ledger Entry")
|
||||||
|
|
||||||
self.common_filter_conditions.append(ple.company == self.company)
|
self.common_filter_conditions.append(ple.company == self.company)
|
||||||
|
|
||||||
if self.get("cost_center") and (get_invoices or get_return_invoices):
|
if self.get("cost_center") and (get_invoices or get_return_invoices):
|
||||||
self.common_filter_conditions.append(ple.cost_center == self.cost_center)
|
self.accounting_dimension_filter_conditions.append(ple.cost_center == self.cost_center)
|
||||||
|
|
||||||
if get_invoices:
|
if get_invoices:
|
||||||
if self.from_invoice_date:
|
if self.from_invoice_date:
|
||||||
self.common_filter_conditions.append(ple.posting_date.gte(self.from_invoice_date))
|
self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_invoice_date))
|
||||||
if self.to_invoice_date:
|
if self.to_invoice_date:
|
||||||
self.common_filter_conditions.append(ple.posting_date.lte(self.to_invoice_date))
|
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_invoice_date))
|
||||||
|
|
||||||
elif get_return_invoices:
|
elif get_return_invoices:
|
||||||
if self.from_payment_date:
|
if self.from_payment_date:
|
||||||
self.common_filter_conditions.append(ple.posting_date.gte(self.from_payment_date))
|
self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_payment_date))
|
||||||
if self.to_payment_date:
|
if self.to_payment_date:
|
||||||
self.common_filter_conditions.append(ple.posting_date.lte(self.to_payment_date))
|
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date))
|
||||||
|
|
||||||
def get_conditions(self, get_payments=False):
|
def get_conditions(self, get_payments=False):
|
||||||
condition = " and company = '{0}' ".format(self.company)
|
condition = " and company = '{0}' ".format(self.company)
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import qb
|
from frappe import qb
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
from frappe.utils import add_days, nowdate
|
from frappe.utils import add_days, flt, nowdate
|
||||||
|
|
||||||
|
from erpnext import get_default_cost_center
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.accounts.party import get_party_account
|
from erpnext.accounts.party import get_party_account
|
||||||
@@ -19,6 +21,8 @@ class TestPaymentReconciliation(FrappeTestCase):
|
|||||||
self.create_company()
|
self.create_company()
|
||||||
self.create_item()
|
self.create_item()
|
||||||
self.create_customer()
|
self.create_customer()
|
||||||
|
self.create_account()
|
||||||
|
self.create_cost_center()
|
||||||
self.clear_old_entries()
|
self.clear_old_entries()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@@ -71,23 +75,33 @@ class TestPaymentReconciliation(FrappeTestCase):
|
|||||||
self.item = item if isinstance(item, str) else item.item_code
|
self.item = item if isinstance(item, str) else item.item_code
|
||||||
|
|
||||||
def create_customer(self):
|
def create_customer(self):
|
||||||
if frappe.db.exists("Customer", "_Test PR Customer"):
|
self.customer = make_customer("_Test PR Customer")
|
||||||
self.customer = "_Test PR Customer"
|
self.customer2 = make_customer("_Test PR Customer 2")
|
||||||
else:
|
self.customer3 = make_customer("_Test PR Customer 3", "EUR")
|
||||||
customer = frappe.new_doc("Customer")
|
self.customer4 = make_customer("_Test PR Customer 4", "EUR")
|
||||||
customer.customer_name = "_Test PR Customer"
|
self.customer5 = make_customer("_Test PR Customer 5", "EUR")
|
||||||
customer.type = "Individual"
|
|
||||||
customer.save()
|
|
||||||
self.customer = customer.name
|
|
||||||
|
|
||||||
if frappe.db.exists("Customer", "_Test PR Customer 2"):
|
def create_account(self):
|
||||||
self.customer2 = "_Test PR Customer 2"
|
account_name = "Debtors EUR"
|
||||||
|
if not frappe.db.get_value(
|
||||||
|
"Account", filters={"account_name": account_name, "company": self.company}
|
||||||
|
):
|
||||||
|
acc = frappe.new_doc("Account")
|
||||||
|
acc.account_name = account_name
|
||||||
|
acc.parent_account = "Accounts Receivable - _PR"
|
||||||
|
acc.company = self.company
|
||||||
|
acc.account_currency = "EUR"
|
||||||
|
acc.account_type = "Receivable"
|
||||||
|
acc.insert()
|
||||||
else:
|
else:
|
||||||
customer = frappe.new_doc("Customer")
|
name = frappe.db.get_value(
|
||||||
customer.customer_name = "_Test PR Customer 2"
|
"Account",
|
||||||
customer.type = "Individual"
|
filters={"account_name": account_name, "company": self.company},
|
||||||
customer.save()
|
fieldname="name",
|
||||||
self.customer2 = customer.name
|
pluck=True,
|
||||||
|
)
|
||||||
|
acc = frappe.get_doc("Account", name)
|
||||||
|
self.debtors_eur = acc.name
|
||||||
|
|
||||||
def create_sales_invoice(
|
def create_sales_invoice(
|
||||||
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||||
@@ -118,7 +132,7 @@ class TestPaymentReconciliation(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
return sinv
|
return sinv
|
||||||
|
|
||||||
def create_payment_entry(self, amount=100, posting_date=nowdate()):
|
def create_payment_entry(self, amount=100, posting_date=nowdate(), customer=None):
|
||||||
"""
|
"""
|
||||||
Helper function to populate default values in payment entry
|
Helper function to populate default values in payment entry
|
||||||
"""
|
"""
|
||||||
@@ -126,7 +140,7 @@ class TestPaymentReconciliation(FrappeTestCase):
|
|||||||
company=self.company,
|
company=self.company,
|
||||||
payment_type="Receive",
|
payment_type="Receive",
|
||||||
party_type="Customer",
|
party_type="Customer",
|
||||||
party=self.customer,
|
party=customer or self.customer,
|
||||||
paid_from=self.debit_to,
|
paid_from=self.debit_to,
|
||||||
paid_to=self.bank,
|
paid_to=self.bank,
|
||||||
paid_amount=amount,
|
paid_amount=amount,
|
||||||
@@ -183,6 +197,22 @@ class TestPaymentReconciliation(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
return je
|
return je
|
||||||
|
|
||||||
|
def create_cost_center(self):
|
||||||
|
# Setup cost center
|
||||||
|
cc_name = "Sub"
|
||||||
|
|
||||||
|
self.main_cc = frappe.get_doc("Cost Center", get_default_cost_center(self.company))
|
||||||
|
|
||||||
|
cc_exists = frappe.db.get_list("Cost Center", filters={"cost_center_name": cc_name})
|
||||||
|
if cc_exists:
|
||||||
|
self.sub_cc = frappe.get_doc("Cost Center", cc_exists[0].name)
|
||||||
|
else:
|
||||||
|
sub_cc = frappe.new_doc("Cost Center")
|
||||||
|
sub_cc.cost_center_name = "Sub"
|
||||||
|
sub_cc.parent_cost_center = self.main_cc.parent_cost_center
|
||||||
|
sub_cc.company = self.main_cc.company
|
||||||
|
self.sub_cc = sub_cc.save()
|
||||||
|
|
||||||
def test_filter_min_max(self):
|
def test_filter_min_max(self):
|
||||||
# check filter condition minimum and maximum amount
|
# check filter condition minimum and maximum amount
|
||||||
self.create_sales_invoice(qty=1, rate=300)
|
self.create_sales_invoice(qty=1, rate=300)
|
||||||
@@ -250,6 +280,41 @@ class TestPaymentReconciliation(FrappeTestCase):
|
|||||||
self.assertEqual(len(pr.get("invoices")), 2)
|
self.assertEqual(len(pr.get("invoices")), 2)
|
||||||
self.assertEqual(len(pr.get("payments")), 2)
|
self.assertEqual(len(pr.get("payments")), 2)
|
||||||
|
|
||||||
|
def test_filter_posting_date_case2(self):
|
||||||
|
"""
|
||||||
|
Posting date should not affect outstanding amount calculation
|
||||||
|
"""
|
||||||
|
|
||||||
|
from_date = add_days(nowdate(), -30)
|
||||||
|
to_date = nowdate()
|
||||||
|
self.create_payment_entry(amount=25, posting_date=from_date).submit()
|
||||||
|
self.create_sales_invoice(rate=25, qty=1, posting_date=to_date)
|
||||||
|
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.from_invoice_date = pr.from_payment_date = from_date
|
||||||
|
pr.to_invoice_date = pr.to_payment_date = to_date
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 1)
|
||||||
|
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [x.as_dict() for x in pr.payments]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
pr.reconcile()
|
||||||
|
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
self.assertEqual(len(pr.invoices), 0)
|
||||||
|
self.assertEqual(len(pr.payments), 0)
|
||||||
|
|
||||||
|
pr.from_invoice_date = pr.from_payment_date = to_date
|
||||||
|
pr.to_invoice_date = pr.to_payment_date = to_date
|
||||||
|
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
self.assertEqual(len(pr.invoices), 0)
|
||||||
|
|
||||||
def test_filter_invoice_limit(self):
|
def test_filter_invoice_limit(self):
|
||||||
# check filter condition - invoice limit
|
# check filter condition - invoice limit
|
||||||
transaction_date = nowdate()
|
transaction_date = nowdate()
|
||||||
@@ -454,3 +519,311 @@ class TestPaymentReconciliation(FrappeTestCase):
|
|||||||
self.assertEqual(len(pr.get("payments")), 1)
|
self.assertEqual(len(pr.get("payments")), 1)
|
||||||
self.assertEqual(pr.get("invoices")[0].outstanding_amount, 20)
|
self.assertEqual(pr.get("invoices")[0].outstanding_amount, 20)
|
||||||
self.assertEqual(pr.get("payments")[0].amount, 20)
|
self.assertEqual(pr.get("payments")[0].amount, 20)
|
||||||
|
|
||||||
|
def test_pr_output_foreign_currency_and_amount(self):
|
||||||
|
# test for currency and amount invoices and payments
|
||||||
|
transaction_date = nowdate()
|
||||||
|
# In EUR
|
||||||
|
amount = 100
|
||||||
|
exchange_rate = 80
|
||||||
|
|
||||||
|
si = self.create_sales_invoice(
|
||||||
|
qty=1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
|
||||||
|
)
|
||||||
|
si.customer = self.customer3
|
||||||
|
si.currency = "EUR"
|
||||||
|
si.conversion_rate = exchange_rate
|
||||||
|
si.debit_to = self.debtors_eur
|
||||||
|
si = si.save().submit()
|
||||||
|
|
||||||
|
cr_note = self.create_sales_invoice(
|
||||||
|
qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
|
||||||
|
)
|
||||||
|
cr_note.customer = self.customer3
|
||||||
|
cr_note.is_return = 1
|
||||||
|
cr_note.currency = "EUR"
|
||||||
|
cr_note.conversion_rate = exchange_rate
|
||||||
|
cr_note.debit_to = self.debtors_eur
|
||||||
|
cr_note = cr_note.save().submit()
|
||||||
|
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.party = self.customer3
|
||||||
|
pr.receivable_payable_account = self.debtors_eur
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 1)
|
||||||
|
|
||||||
|
self.assertEqual(pr.invoices[0].amount, amount)
|
||||||
|
self.assertEqual(pr.invoices[0].currency, "EUR")
|
||||||
|
self.assertEqual(pr.payments[0].amount, amount)
|
||||||
|
self.assertEqual(pr.payments[0].currency, "EUR")
|
||||||
|
|
||||||
|
cr_note.cancel()
|
||||||
|
|
||||||
|
pay = self.create_payment_entry(
|
||||||
|
amount=amount, posting_date=transaction_date, customer=self.customer3
|
||||||
|
)
|
||||||
|
pay.paid_from = self.debtors_eur
|
||||||
|
pay.paid_from_account_currency = "EUR"
|
||||||
|
pay.source_exchange_rate = exchange_rate
|
||||||
|
pay.received_amount = exchange_rate * amount
|
||||||
|
pay = pay.save().submit()
|
||||||
|
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 1)
|
||||||
|
self.assertEqual(pr.payments[0].amount, amount)
|
||||||
|
self.assertEqual(pr.payments[0].currency, "EUR")
|
||||||
|
|
||||||
|
def test_difference_amount_via_journal_entry(self):
|
||||||
|
# Make Sale Invoice
|
||||||
|
si = self.create_sales_invoice(
|
||||||
|
qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
|
||||||
|
)
|
||||||
|
si.customer = self.customer4
|
||||||
|
si.currency = "EUR"
|
||||||
|
si.conversion_rate = 85
|
||||||
|
si.debit_to = self.debtors_eur
|
||||||
|
si.save().submit()
|
||||||
|
|
||||||
|
# Make payment using Journal Entry
|
||||||
|
je1 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 100, nowdate())
|
||||||
|
je1.multi_currency = 1
|
||||||
|
je1.accounts[0].exchange_rate = 1
|
||||||
|
je1.accounts[0].credit_in_account_currency = 0
|
||||||
|
je1.accounts[0].credit = 0
|
||||||
|
je1.accounts[0].debit_in_account_currency = 8000
|
||||||
|
je1.accounts[0].debit = 8000
|
||||||
|
je1.accounts[1].party_type = "Customer"
|
||||||
|
je1.accounts[1].party = self.customer4
|
||||||
|
je1.accounts[1].exchange_rate = 80
|
||||||
|
je1.accounts[1].credit_in_account_currency = 100
|
||||||
|
je1.accounts[1].credit = 8000
|
||||||
|
je1.accounts[1].debit_in_account_currency = 0
|
||||||
|
je1.accounts[1].debit = 0
|
||||||
|
je1.save()
|
||||||
|
je1.submit()
|
||||||
|
|
||||||
|
je2 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 200, nowdate())
|
||||||
|
je2.multi_currency = 1
|
||||||
|
je2.accounts[0].exchange_rate = 1
|
||||||
|
je2.accounts[0].credit_in_account_currency = 0
|
||||||
|
je2.accounts[0].credit = 0
|
||||||
|
je2.accounts[0].debit_in_account_currency = 16000
|
||||||
|
je2.accounts[0].debit = 16000
|
||||||
|
je2.accounts[1].party_type = "Customer"
|
||||||
|
je2.accounts[1].party = self.customer4
|
||||||
|
je2.accounts[1].exchange_rate = 80
|
||||||
|
je2.accounts[1].credit_in_account_currency = 200
|
||||||
|
je1.accounts[1].credit = 16000
|
||||||
|
je1.accounts[1].debit_in_account_currency = 0
|
||||||
|
je1.accounts[1].debit = 0
|
||||||
|
je2.save()
|
||||||
|
je2.submit()
|
||||||
|
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.party = self.customer4
|
||||||
|
pr.receivable_payable_account = self.debtors_eur
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 2)
|
||||||
|
|
||||||
|
# Test exact payment allocation
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [pr.payments[0].as_dict()]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
|
||||||
|
self.assertEqual(pr.allocation[0].allocated_amount, 100)
|
||||||
|
self.assertEqual(pr.allocation[0].difference_amount, -500)
|
||||||
|
|
||||||
|
# Test partial payment allocation (with excess payment entry)
|
||||||
|
pr.set("allocation", [])
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [pr.payments[1].as_dict()]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
pr.allocation[0].difference_account = "Exchange Gain/Loss - _PR"
|
||||||
|
|
||||||
|
self.assertEqual(pr.allocation[0].allocated_amount, 100)
|
||||||
|
self.assertEqual(pr.allocation[0].difference_amount, -500)
|
||||||
|
|
||||||
|
# Check if difference journal entry gets generated for difference amount after reconciliation
|
||||||
|
pr.reconcile()
|
||||||
|
total_debit_amount = frappe.db.get_all(
|
||||||
|
"Journal Entry Account",
|
||||||
|
{"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name},
|
||||||
|
"sum(debit) as amount",
|
||||||
|
group_by="reference_name",
|
||||||
|
)[0].amount
|
||||||
|
|
||||||
|
self.assertEqual(flt(total_debit_amount, 2), -500)
|
||||||
|
|
||||||
|
def test_difference_amount_via_payment_entry(self):
|
||||||
|
# Make Sale Invoice
|
||||||
|
si = self.create_sales_invoice(
|
||||||
|
qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
|
||||||
|
)
|
||||||
|
si.customer = self.customer5
|
||||||
|
si.currency = "EUR"
|
||||||
|
si.conversion_rate = 85
|
||||||
|
si.debit_to = self.debtors_eur
|
||||||
|
si.save().submit()
|
||||||
|
|
||||||
|
# Make payment using Payment Entry
|
||||||
|
pe1 = create_payment_entry(
|
||||||
|
company=self.company,
|
||||||
|
payment_type="Receive",
|
||||||
|
party_type="Customer",
|
||||||
|
party=self.customer5,
|
||||||
|
paid_from=self.debtors_eur,
|
||||||
|
paid_to=self.bank,
|
||||||
|
paid_amount=100,
|
||||||
|
)
|
||||||
|
|
||||||
|
pe1.source_exchange_rate = 80
|
||||||
|
pe1.received_amount = 8000
|
||||||
|
pe1.save()
|
||||||
|
pe1.submit()
|
||||||
|
|
||||||
|
pe2 = create_payment_entry(
|
||||||
|
company=self.company,
|
||||||
|
payment_type="Receive",
|
||||||
|
party_type="Customer",
|
||||||
|
party=self.customer5,
|
||||||
|
paid_from=self.debtors_eur,
|
||||||
|
paid_to=self.bank,
|
||||||
|
paid_amount=200,
|
||||||
|
)
|
||||||
|
|
||||||
|
pe2.source_exchange_rate = 80
|
||||||
|
pe2.received_amount = 16000
|
||||||
|
pe2.save()
|
||||||
|
pe2.submit()
|
||||||
|
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.party = self.customer5
|
||||||
|
pr.receivable_payable_account = self.debtors_eur
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
self.assertEqual(len(pr.invoices), 1)
|
||||||
|
self.assertEqual(len(pr.payments), 2)
|
||||||
|
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [pr.payments[0].as_dict()]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
|
||||||
|
self.assertEqual(pr.allocation[0].allocated_amount, 100)
|
||||||
|
self.assertEqual(pr.allocation[0].difference_amount, -500)
|
||||||
|
|
||||||
|
pr.set("allocation", [])
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
invoices = [x.as_dict() for x in pr.invoices]
|
||||||
|
payments = [pr.payments[1].as_dict()]
|
||||||
|
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
|
||||||
|
|
||||||
|
self.assertEqual(pr.allocation[0].allocated_amount, 100)
|
||||||
|
self.assertEqual(pr.allocation[0].difference_amount, -500)
|
||||||
|
|
||||||
|
def test_differing_cost_center_on_invoice_and_payment(self):
|
||||||
|
"""
|
||||||
|
Cost Center filter should not affect outstanding amount calculation
|
||||||
|
"""
|
||||||
|
|
||||||
|
si = self.create_sales_invoice(qty=1, rate=100, do_not_submit=True)
|
||||||
|
si.cost_center = self.main_cc.name
|
||||||
|
si.submit()
|
||||||
|
pr = get_payment_entry(si.doctype, si.name)
|
||||||
|
pr.cost_center = self.sub_cc.name
|
||||||
|
pr = pr.save().submit()
|
||||||
|
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.cost_center = self.main_cc.name
|
||||||
|
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
# check PR tool output
|
||||||
|
self.assertEqual(len(pr.get("invoices")), 0)
|
||||||
|
self.assertEqual(len(pr.get("payments")), 0)
|
||||||
|
|
||||||
|
def test_cost_center_filter_on_vouchers(self):
|
||||||
|
"""
|
||||||
|
Test Cost Center filter is applied on Invoices, Payment Entries and Journals
|
||||||
|
"""
|
||||||
|
transaction_date = nowdate()
|
||||||
|
rate = 100
|
||||||
|
|
||||||
|
# 'Main - PR' Cost Center
|
||||||
|
si1 = self.create_sales_invoice(
|
||||||
|
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
|
||||||
|
)
|
||||||
|
si1.cost_center = self.main_cc.name
|
||||||
|
si1.submit()
|
||||||
|
|
||||||
|
pe1 = self.create_payment_entry(posting_date=transaction_date, amount=rate)
|
||||||
|
pe1.cost_center = self.main_cc.name
|
||||||
|
pe1 = pe1.save().submit()
|
||||||
|
|
||||||
|
je1 = self.create_journal_entry(self.bank, self.debit_to, 100, transaction_date)
|
||||||
|
je1.accounts[0].cost_center = self.main_cc.name
|
||||||
|
je1.accounts[1].cost_center = self.main_cc.name
|
||||||
|
je1.accounts[1].party_type = "Customer"
|
||||||
|
je1.accounts[1].party = self.customer
|
||||||
|
je1 = je1.save().submit()
|
||||||
|
|
||||||
|
# 'Sub - PR' Cost Center
|
||||||
|
si2 = self.create_sales_invoice(
|
||||||
|
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
|
||||||
|
)
|
||||||
|
si2.cost_center = self.sub_cc.name
|
||||||
|
si2.submit()
|
||||||
|
|
||||||
|
pe2 = self.create_payment_entry(posting_date=transaction_date, amount=rate)
|
||||||
|
pe2.cost_center = self.sub_cc.name
|
||||||
|
pe2 = pe2.save().submit()
|
||||||
|
|
||||||
|
je2 = self.create_journal_entry(self.bank, self.debit_to, 100, transaction_date)
|
||||||
|
je2.accounts[0].cost_center = self.sub_cc.name
|
||||||
|
je2.accounts[1].cost_center = self.sub_cc.name
|
||||||
|
je2.accounts[1].party_type = "Customer"
|
||||||
|
je2.accounts[1].party = self.customer
|
||||||
|
je2 = je2.save().submit()
|
||||||
|
|
||||||
|
pr = self.create_payment_reconciliation()
|
||||||
|
pr.cost_center = self.main_cc.name
|
||||||
|
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
# check PR tool output
|
||||||
|
self.assertEqual(len(pr.get("invoices")), 1)
|
||||||
|
self.assertEqual(pr.get("invoices")[0].get("invoice_number"), si1.name)
|
||||||
|
self.assertEqual(len(pr.get("payments")), 2)
|
||||||
|
payment_vouchers = [x.get("reference_name") for x in pr.get("payments")]
|
||||||
|
self.assertCountEqual(payment_vouchers, [pe1.name, je1.name])
|
||||||
|
|
||||||
|
# Change cost center
|
||||||
|
pr.cost_center = self.sub_cc.name
|
||||||
|
|
||||||
|
pr.get_unreconciled_entries()
|
||||||
|
|
||||||
|
# check PR tool output
|
||||||
|
self.assertEqual(len(pr.get("invoices")), 1)
|
||||||
|
self.assertEqual(pr.get("invoices")[0].get("invoice_number"), si2.name)
|
||||||
|
self.assertEqual(len(pr.get("payments")), 2)
|
||||||
|
payment_vouchers = [x.get("reference_name") for x in pr.get("payments")]
|
||||||
|
self.assertCountEqual(payment_vouchers, [je2.name, pe2.name])
|
||||||
|
|
||||||
|
|
||||||
|
def make_customer(customer_name, currency=None):
|
||||||
|
if not frappe.db.exists("Customer", customer_name):
|
||||||
|
customer = frappe.new_doc("Customer")
|
||||||
|
customer.customer_name = customer_name
|
||||||
|
customer.type = "Individual"
|
||||||
|
|
||||||
|
if currency:
|
||||||
|
customer.default_currency = currency
|
||||||
|
customer.save()
|
||||||
|
return customer.name
|
||||||
|
else:
|
||||||
|
return customer_name
|
||||||
|
|||||||
@@ -20,7 +20,9 @@
|
|||||||
"section_break_5",
|
"section_break_5",
|
||||||
"difference_amount",
|
"difference_amount",
|
||||||
"column_break_7",
|
"column_break_7",
|
||||||
"difference_account"
|
"difference_account",
|
||||||
|
"exchange_rate",
|
||||||
|
"currency"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -37,7 +39,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Allocated Amount",
|
"label": "Allocated Amount",
|
||||||
"options": "Currency",
|
"options": "currency",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -112,7 +114,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Unreconciled Amount",
|
"label": "Unreconciled Amount",
|
||||||
"options": "Currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -120,7 +122,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"options": "Currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -129,11 +131,24 @@
|
|||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Reference Row",
|
"label": "Reference Row",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Currency",
|
||||||
|
"options": "Currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "exchange_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Exchange Rate",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-10-06 11:48:59.616562",
|
"modified": "2022-12-24 21:01:14.882747",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Allocation",
|
"name": "Payment Reconciliation Allocation",
|
||||||
@@ -141,5 +156,6 @@
|
|||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
"col_break1",
|
"col_break1",
|
||||||
"amount",
|
"amount",
|
||||||
"outstanding_amount",
|
"outstanding_amount",
|
||||||
"currency"
|
"currency",
|
||||||
|
"exchange_rate"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -62,11 +63,17 @@
|
|||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Currency",
|
"label": "Currency",
|
||||||
"options": "Currency"
|
"options": "Currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "exchange_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Exchange Rate"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-08-24 22:42:40.923179",
|
"modified": "2022-11-08 18:18:02.502149",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Invoice",
|
"name": "Payment Reconciliation Invoice",
|
||||||
@@ -75,5 +82,6 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,8 @@
|
|||||||
"difference_amount",
|
"difference_amount",
|
||||||
"sec_break1",
|
"sec_break1",
|
||||||
"remark",
|
"remark",
|
||||||
"currency"
|
"currency",
|
||||||
|
"exchange_rate"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -91,11 +92,17 @@
|
|||||||
"label": "Difference Amount",
|
"label": "Difference Amount",
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "exchange_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Exchange Rate"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-08-30 10:51:48.140062",
|
"modified": "2022-11-08 18:18:36.268760",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Payment",
|
"name": "Payment Reconciliation Payment",
|
||||||
@@ -103,5 +110,6 @@
|
|||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ frappe.ui.form.on("Payment Request", "refresh", function(frm) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!frm.doc.payment_gateway_account && frm.doc.status == "Initiated") {
|
if((!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") && frm.doc.status == "Initiated") {
|
||||||
frm.add_custom_button(__('Create Payment Entry'), function(){
|
frm.add_custom_button(__('Create Payment Entry'), function(){
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.payment_request.payment_request.make_payment_entry",
|
method: "erpnext.accounts.doctype.payment_request.payment_request.make_payment_entry",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user