Compare commits

..

1 Commits

Author SHA1 Message Date
Ankush Menat
e8571bac00 fix: Correctly extract last message
frappe.message_log now contains plain dictionary and not JSON strings,
so no need to load them.
2023-10-20 15:18:04 +05:30
2818 changed files with 643854 additions and 1696997 deletions

View File

@@ -9,13 +9,6 @@ trim_trailing_whitespace = true
charset = utf-8 charset = utf-8
# python, js indentation settings # python, js indentation settings
[{*.py,*.js,*.vue,*.css,*.scss,*.html}] [{*.py,*.js}]
indent_style = tab indent_style = tab
indent_size = 4 indent_size = 4
max_line_length = 110
# JSON files - mostly doctype schema files
[{*.json}]
insert_final_newline = false
indent_style = space
indent_size = 1

View File

@@ -124,7 +124,6 @@
"beforeEach": true, "beforeEach": true,
"onScan": true, "onScan": true,
"extend_cscript": true, "extend_cscript": true,
"localforage": true, "localforage": true
"Plaid": true
} }
} }

View File

@@ -28,17 +28,4 @@ b147b85e6ac19a9220cd1e2958a6ebd99373283a
494bd9ef78313436f0424b918f200dab8fc7c20b 494bd9ef78313436f0424b918f200dab8fc7c20b
# bulk format python code with black # bulk format python code with black
baec607ff5905b1c67531096a9cf50ec7ff00a5d baec607ff5905b1c67531096a9cf50ec7ff00a5d
# bulk refactor with sourcery
eb9ee3f79b94e594fc6dfa4f6514580e125eee8c
# js formatting
ec74a5e56617bbd76ac402451468fd4668af543d
# ruff formatting
a308792ee7fda18a681e9181f4fd00b36385bc23
# noisy typing refactoring of get_item_details
7b7211ac79c248a79ba8a999ff34e734d874c0ae
d827ed21adc7b36047e247cbb0dc6388d048a7f9

View File

@@ -1,7 +1,7 @@
import sys import sys
import requests
from urllib.parse import urlparse from urllib.parse import urlparse
import requests
WEBSITE_REPOS = [ WEBSITE_REPOS = [
"erpnext_com", "erpnext_com",
@@ -36,7 +36,11 @@ def is_documentation_link(word: str) -> bool:
def contains_documentation_link(body: str) -> bool: def contains_documentation_link(body: str) -> bool:
return any(is_documentation_link(word) for line in body.splitlines() for word in line.split()) 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]": def check_pull_request(number: str) -> "tuple[int, str]":
@@ -49,7 +53,12 @@ def check_pull_request(number: str) -> "tuple[int, str]":
head_sha = (payload.get("head") or {}).get("sha") head_sha = (payload.get("head") or {}).get("sha")
body = (payload.get("body") or "").lower() body = (payload.get("body") or "").lower()
if not title.startswith("feat") or not head_sha or "no-docs" in body or "backport" in body: if (
not title.startswith("feat")
or not head_sha
or "no-docs" in body
or "backport" in body
):
return 0, "Skipping documentation checks... 🏃" return 0, "Skipping documentation checks... 🏃"
if contains_documentation_link(body): if contains_documentation_link(body):

View File

@@ -12,16 +12,9 @@ pip install frappe-bench
githubbranch=${GITHUB_BASE_REF:-${GITHUB_REF##*/}} githubbranch=${GITHUB_BASE_REF:-${GITHUB_REF##*/}}
frappeuser=${FRAPPE_USER:-"frappe"} frappeuser=${FRAPPE_USER:-"frappe"}
frappecommitish=${FRAPPE_BRANCH:-$githubbranch} frappebranch=${FRAPPE_BRANCH:-$githubbranch}
mkdir frappe
pushd frappe
git init
git remote add origin "https://github.com/${frappeuser}/frappe"
git fetch origin "${frappecommitish}" --depth 1
git checkout FETCH_HEAD
popd
git clone "https://github.com/${frappeuser}/frappe" --branch "${frappebranch}" --depth 1
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
mkdir ~/frappe-bench/sites/test_site mkdir ~/frappe-bench/sites/test_site

View File

@@ -2,9 +2,7 @@ import re
import sys import sys
errors_encounter = 0 errors_encounter = 0
pattern = re.compile( pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)"
)
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]") words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]")
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}") start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}")
f_string_pattern = re.compile(r"_\(f[\"']") f_string_pattern = re.compile(r"_\(f[\"']")
@@ -12,14 +10,14 @@ starts_with_f_pattern = re.compile(r"_\(f")
# skip first argument # skip first argument
files = sys.argv[1:] files = sys.argv[1:]
files_to_scan = [_file for _file in files if _file.endswith((".py", ".js"))] files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))]
for _file in files_to_scan: for _file in files_to_scan:
with open(_file) as f: with open(_file, 'r') as f:
print(f"Checking: {_file}") print(f'Checking: {_file}')
file_lines = f.readlines() file_lines = f.readlines()
for line_number, line in enumerate(file_lines, 1): for line_number, line in enumerate(file_lines, 1):
if "frappe-lint: disable-translate" in line: if 'frappe-lint: disable-translate' in line:
continue continue
start_matches = start_pattern.search(line) start_matches = start_pattern.search(line)
@@ -30,9 +28,7 @@ for _file in files_to_scan:
has_f_string = f_string_pattern.search(line) has_f_string = f_string_pattern.search(line)
if has_f_string: if has_f_string:
errors_encounter += 1 errors_encounter += 1
print( print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}')
f"\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}"
)
continue continue
else: else:
continue continue
@@ -40,29 +36,25 @@ for _file in files_to_scan:
match = pattern.search(line) match = pattern.search(line)
error_found = False error_found = False
if not match and line.endswith((",\n", "[\n")): if not match and line.endswith((',\n', '[\n')):
# concat remaining text to validate multiline pattern # concat remaining text to validate multiline pattern
line = "".join(file_lines[line_number - 1 :]) line = "".join(file_lines[line_number - 1:])
line = line[start_matches.start() + 1 :] line = line[start_matches.start() + 1:]
match = pattern.match(line) match = pattern.match(line)
if not match: if not match:
error_found = True error_found = True
print(f"\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}") print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}')
if not error_found and not words_pattern.search(line): if not error_found and not words_pattern.search(line):
error_found = True error_found = True
print( print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}')
f"\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}"
)
if error_found: if error_found:
errors_encounter += 1 errors_encounter += 1
if errors_encounter > 0: if errors_encounter > 0:
print( print('\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.')
'\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.'
)
sys.exit(1) sys.exit(1)
else: else:
print("\nGood To Go!") print('\nGood To Go!')

View File

@@ -1,40 +0,0 @@
#!/bin/bash
set -e
cd ~ || exit
echo "Setting Up Bench..."
pip install frappe-bench
bench -v init frappe-bench --skip-assets --skip-redis-config-generation --python "$(which python)"
cd ./frappe-bench || exit
echo "Get ERPNext..."
bench get-app --skip-assets erpnext "${GITHUB_WORKSPACE}"
echo "Generating POT file..."
bench generate-pot-file --app erpnext
cd ./apps/erpnext || exit
echo "Configuring git user..."
git config user.email "developers@erpnext.com"
git config user.name "frappe-pr-bot"
echo "Setting the correct git remote..."
# Here, the git remote is a local file path by default. Let's change it to the upstream repo.
git remote set-url upstream https://github.com/frappe/erpnext.git
echo "Creating a new branch..."
isodate=$(date -u +"%Y-%m-%d")
branch_name="pot_${BASE_BRANCH}_${isodate}"
git checkout -b "${branch_name}"
echo "Commiting changes..."
git add erpnext/locale/main.pot
git commit -m "chore: update POT file"
gh auth setup-git
git push -u upstream "${branch_name}"
echo "Creating a PR..."
gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" --reviewer ${PR_REVIEWER} -R frappe/erpnext

8
.github/stale.yml vendored
View File

@@ -12,14 +12,6 @@ exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false) # Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true exemptMilestones: true
# Skip the stale action for draft PRs
exemptDraftPr: true
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- hotfix
- no-stale
pulls: pulls:
daysUntilStale: 15 daysUntilStale: 15
daysUntilClose: 3 daysUntilClose: 3

View File

@@ -21,6 +21,6 @@ jobs:
- name: Run backport - name: Run backport
uses: ./actions/backport uses: ./actions/backport
with: with:
token: ${{secrets.RELEASE_TOKEN}} token: ${{secrets.BACKPORT_BOT_TOKEN}}
labelsToAdd: "backport" labelsToAdd: "backport"
title: "{{originalTitle}}" title: "{{originalTitle}}"

View File

@@ -1,39 +0,0 @@
# This workflow is agnostic to branches. Only maintain on develop branch.
# To add/remove branches just modify the matrix.
name: Regenerate POT file (translatable strings)
on:
schedule:
# 9:30 UTC => 3 PM IST Sunday
- cron: "30 9 * * 0"
workflow_dispatch:
jobs:
regenerate-pot-file:
name: Regenerate POT file
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
branch: ["develop"]
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Run script to update POT file
run: |
bash ${GITHUB_WORKSPACE}/.github/helper/update_pot_file.sh
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
BASE_BRANCH: ${{ matrix.branch }}
PR_REVIEWER: barredterra # change to your GitHub username if you copied this file

View File

@@ -15,7 +15,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
version: ["14", "15"] version: ["13", "14"]
steps: steps:
- uses: octokit/request-action@v2.x - uses: octokit/request-action@v2.x

View File

@@ -20,18 +20,6 @@ jobs:
- name: Install and Run Pre-commit - name: Install and Run Pre-commit
uses: pre-commit/action@v3.0.0 uses: pre-commit/action@v3.0.0
semgrep:
name: semgrep
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: pip
- 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

View File

@@ -1,21 +0,0 @@
name: 'Lock threads'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-inactive-days: 14
pr-inactive-days: 14

View File

@@ -32,7 +32,7 @@ jobs:
steps: steps:
- name: Clone - name: Clone
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Check for valid Python & Merge Conflicts - name: Check for valid Python & Merge Conflicts
run: | run: |
@@ -43,12 +43,12 @@ jobs:
fi fi
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: "actions/setup-python@v4"
with: with:
python-version: '3.11' python-version: '3.10'
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v2
with: with:
node-version: 18 node-version: 18
check-latest: true check-latest: true
@@ -57,7 +57,7 @@ jobs:
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip - name: Cache pip
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
@@ -66,7 +66,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: Cache node modules - name: Cache node modules
uses: actions/cache@v4 uses: actions/cache@v2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
with: with:
@@ -81,7 +81,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v4 - uses: actions/cache@v2
id: yarn-cache id: yarn-cache
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -137,8 +137,7 @@ jobs:
update_to_version 15 update_to_version 15
echo "Updating to latest version" echo "Updating to latest version"
git -C "apps/frappe" fetch --depth 1 upstream "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
git -C "apps/frappe" checkout -q -f FETCH_HEAD
git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA" git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
pgrep honcho | xargs kill pgrep honcho | xargs kill

View File

@@ -1,22 +0,0 @@
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
name: Skipped Patch Test
on:
pull_request:
paths:
- "**.js"
- "**.css"
- "**.md"
- "**.html"
- "**.csv"
jobs:
test:
runs-on: ubuntu-latest
name: Patch Test
steps:
- name: Pass skipped tests unconditionally
run: "echo Skipped"

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: 20 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

View File

@@ -29,11 +29,7 @@ jobs:
steps: steps:
- name: Update notes - name: Update notes
run: | run: |
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG \ NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
| jq -r '.body' \
| sed -E '/^\* (chore|ci|test|docs|style)/d' \
| sed -E 's/by @mergify //'
)
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id') RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id')
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES" gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES"

View File

@@ -1,130 +0,0 @@
name: Individual
on:
workflow_dispatch:
concurrency:
group: server-individual-tests-develop-${{ github.event_name }}-${{ github.event.number || github.event_name == 'workflow_dispatch' && github.run_id || '' }}
cancel-in-progress: false
jobs:
discover:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Clone
uses: actions/checkout@v4
- id: set-matrix
run: |
# Use grep and find to get the list of test files
matrix=$(find . -path '*/doctype/*/test_*.py' | xargs grep -l 'def test_' | awk '{
# Remove ./ prefix, file extension, and replace / with .
gsub(/^\.\//, "", $0)
gsub(/\.py$/, "", $0)
gsub(/\//, ".", $0)
# Add to array
tests[NR] = $0
}
END {
# Start JSON array
printf "{\n \"include\": [\n"
# Loop through array and create JSON objects
for (i=1; i<=NR; i++) {
printf " {\"test\": \"%s\"}", tests[i]
if (i < NR) printf ","
printf "\n"
}
# Close JSON array
printf " ]\n}"
}')
# Output the matrix
echo "matrix=$(echo "$matrix" | jq -c)" >> $GITHUB_OUTPUT
# For debugging (optional)
echo "Generated matrix:"
echo "$matrix"
test:
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 60
env:
NODE_ENV: "production"
strategy:
fail-fast: false
matrix: ${{fromJson(needs.discover.outputs.matrix)}}
name: Test
services:
mysql:
image: mariadb:10.6
env:
MARIADB_ROOT_PASSWORD: 'root'
ports:
- 3306:3306
options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Clone
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
check-latest: true
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
TYPE: server
FRAPPE_USER: ${{ github.event.inputs.user }}
FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
- name: Run Tests
run: 'cd ~/frappe-bench/ && bench --site test_site run-tests --app erpnext --module ${{ matrix.test }}'

View File

@@ -1,24 +0,0 @@
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
name: Skipped Tests
on:
pull_request:
paths:
- "**.js"
- "**.css"
- "**.md"
- "**.html"
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
container: [1, 2, 3, 4]
name: Python Unit Tests
steps:
- name: Pass skipped tests unconditionally
run: "echo Skipped"

View File

@@ -1,8 +1,6 @@
name: Server (Mariadb) name: Server (Mariadb)
on: on:
repository_dispatch:
types: [frappe-framework-change]
pull_request: pull_request:
paths-ignore: paths-ignore:
- '**.js' - '**.js'
@@ -33,9 +31,6 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60 timeout-minutes: 60
env:
NODE_ENV: "production"
WITH_COVERAGE: ${{ github.event_name != 'pull_request' }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -56,12 +51,12 @@ jobs:
steps: steps:
- name: Clone - name: Clone
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: '3.12' python-version: '3.11'
- name: Check for valid Python & Merge Conflicts - name: Check for valid Python & Merge Conflicts
run: | run: |
@@ -72,7 +67,7 @@ jobs:
fi fi
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v2
with: with:
node-version: 18 node-version: 18
check-latest: true check-latest: true
@@ -81,7 +76,7 @@ jobs:
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip - name: Cache pip
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
@@ -90,7 +85,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: Cache node modules - name: Cache node modules
uses: actions/cache@v4 uses: actions/cache@v2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
with: with:
@@ -105,7 +100,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v4 - uses: actions/cache@v2
id: yarn-cache id: yarn-cache
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -119,14 +114,14 @@ jobs:
DB: mariadb DB: mariadb
TYPE: server TYPE: server
FRAPPE_USER: ${{ github.event.inputs.user }} FRAPPE_USER: ${{ github.event.inputs.user }}
FRAPPE_BRANCH: ${{ github.event.client_payload.sha || 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 --total-builds ${{ strategy.job-total }} --build-number ${{ matrix.container }}' 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
CAPTURE_COVERAGE: ${{ github.event_name != 'pull_request' }} CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
- name: Show bench output - name: Show bench output
if: ${{ always() }} if: ${{ always() }}
@@ -134,7 +129,6 @@ jobs:
- name: Upload coverage data - name: Upload coverage data
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
if: github.event_name != 'pull_request'
with: with:
name: coverage-${{ matrix.container }} name: coverage-${{ matrix.container }}
path: /home/runner/frappe-bench/sites/coverage.xml path: /home/runner/frappe-bench/sites/coverage.xml
@@ -143,18 +137,16 @@ jobs:
name: Coverage Wrap Up name: Coverage Wrap Up
needs: test needs: test
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Clone - name: Clone
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
- name: Upload coverage data - name: Upload coverage data
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v2
with: with:
name: MariaDB name: MariaDB
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true fail_ci_if_error: true
verbose: true verbose: true

View File

@@ -41,12 +41,12 @@ jobs:
steps: steps:
- name: Clone - name: Clone
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: '3.12' python-version: '3.10'
- name: Check for valid Python & Merge Conflicts - name: Check for valid Python & Merge Conflicts
run: | run: |
@@ -57,7 +57,7 @@ jobs:
fi fi
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v2
with: with:
node-version: 18 node-version: 18
check-latest: true check-latest: true
@@ -66,7 +66,7 @@ jobs:
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip - name: Cache pip
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
@@ -75,7 +75,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: Cache node modules - name: Cache node modules
uses: actions/cache@v4 uses: actions/cache@v2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
with: with:
@@ -90,7 +90,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v4 - uses: actions/cache@v2
id: yarn-cache id: yarn-cache
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}

6
.gitignore vendored
View File

@@ -2,6 +2,7 @@
*.py~ *.py~
.DS_Store .DS_Store
conf.py conf.py
locale
latest_updates.json latest_updates.json
.wnf-lang-status .wnf-lang-status
*.egg-info *.egg-info
@@ -14,8 +15,5 @@ __pycache__
*~ *~
.idea/ .idea/
.vscode/ .vscode/
.helix/
node_modules/ node_modules/
.backportrc.json .backportrc.json
# Aider AI Chat
.aider*

View File

@@ -5,7 +5,7 @@ fail_fast: false
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0 rev: v4.0.1
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
files: "erpnext.*" files: "erpnext.*"
@@ -15,27 +15,6 @@ repos:
args: ['--branch', 'develop'] args: ['--branch', 'develop']
- id: check-merge-conflict - id: check-merge-conflict
- id: check-ast - id: check-ast
- id: check-json
- id: check-toml
- id: check-yaml
- id: debug-statements
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.7.1
hooks:
- id: prettier
types_or: [javascript, vue, scss]
# Ignore any files that might contain jinja / bundles
exclude: |
(?x)^(
erpnext/public/dist/.*|
cypress/.*|
.*node_modules.*|
.*boilerplate.*|
erpnext/public/js/controllers/.*|
erpnext/templates/pages/order.js|
erpnext/templates/includes/.*
)$
- repo: https://github.com/pre-commit/mirrors-eslint - repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.44.0 rev: v8.44.0
@@ -55,18 +34,29 @@ repos:
erpnext/templates/includes/.* erpnext/templates/includes/.*
)$ )$
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/PyCQA/flake8
rev: v0.2.0 rev: 6.0.0
hooks: hooks:
- id: ruff - id: flake8
name: "Run ruff import sorter" additional_dependencies: [
args: ["--select=I", "--fix"] 'flake8-bugbear',
'flake8-tuple',
]
args: ['--config', '.github/helper/.flake8_strict']
exclude: ".*setup.py$"
- id: ruff - repo: https://github.com/adityahase/black
name: "Run ruff linter" rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
hooks:
- id: black
additional_dependencies: ['click==8.0.4']
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
exclude: ".*setup.py$"
- id: ruff-format
name: "Run ruff formatter"
ci: ci:
autoupdate_schedule: weekly autoupdate_schedule: weekly

View File

@@ -3,22 +3,22 @@
# 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/ @ruthra-kumar erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @khushi8112 erpnext/assets/ @anandbaburajan @deepeshgarg007
erpnext/regional @ruthra-kumar erpnext/regional @deepeshgarg007 @ruthra-kumar
erpnext/selling @ruthra-kumar erpnext/selling @deepeshgarg007 @ruthra-kumar
erpnext/support/ @ruthra-kumar erpnext/support/ @deepeshgarg007
pos* pos*
erpnext/buying/ @rohitwaghchaure erpnext/buying/ @rohitwaghchaure @s-aga-r
erpnext/maintenance/ @rohitwaghchaure erpnext/maintenance/ @rohitwaghchaure @s-aga-r
erpnext/manufacturing/ @rohitwaghchaure erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
erpnext/quality_management/ @rohitwaghchaure erpnext/quality_management/ @rohitwaghchaure @s-aga-r
erpnext/stock/ @rohitwaghchaure erpnext/stock/ @rohitwaghchaure @s-aga-r
erpnext/subcontracting @rohitwaghchaure erpnext/subcontracting @rohitwaghchaure @s-aga-r
erpnext/controllers/ @ruthra-kumar @rohitwaghchaure erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure
erpnext/patches/ @ruthra-kumar erpnext/patches/ @deepeshgarg007
.github/ @ruthra-kumar .github/ @deepeshgarg007
pyproject.toml @akhilnarang pyproject.toml @phot0n

View File

@@ -7,7 +7,8 @@
<p>ERP made simple</p> <p>ERP made simple</p>
</p> </p>
[![CI](https://github.com/frappe/erpnext/actions/workflows/server-tests-mariadb.yml/badge.svg?event=schedule)](https://github.com/frappe/erpnext/actions/workflows/server-tests-mariadb.yml) [![CI](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml/badge.svg?branch=develop)](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml)
[![UI](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml/badge.svg?branch=develop&event=schedule)](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml)
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext) [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
[![codecov](https://codecov.io/gh/frappe/erpnext/branch/develop/graph/badge.svg?token=0TwvyUg3I5)](https://codecov.io/gh/frappe/erpnext) [![codecov](https://codecov.io/gh/frappe/erpnext/branch/develop/graph/badge.svg?token=0TwvyUg3I5)](https://codecov.io/gh/frappe/erpnext)
[![docker pulls](https://img.shields.io/docker/pulls/frappe/erpnext-worker.svg)](https://hub.docker.com/r/frappe/erpnext-worker) [![docker pulls](https://img.shields.io/docker/pulls/frappe/erpnext-worker.svg)](https://hub.docker.com/r/frappe/erpnext-worker)
@@ -72,6 +73,8 @@ New passwords will be created for the ERPNext "Administrator" user, the MariaDB
1. [Issue Guidelines](https://github.com/frappe/erpnext/wiki/Issue-Guidelines) 1. [Issue Guidelines](https://github.com/frappe/erpnext/wiki/Issue-Guidelines)
1. [Report Security Vulnerabilities](https://erpnext.com/security) 1. [Report Security Vulnerabilities](https://erpnext.com/security)
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines) 1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
1. [Translations](https://translate.erpnext.com)
## License ## License

View File

@@ -1,3 +0,0 @@
**/setup/setup_wizard/data/uom_data.json,erpnext.gettext.extractors.uom_data.extract
**/setup/doctype/incoterm/incoterms.csv,erpnext.gettext.extractors.incoterms.extract
**/setup/setup_wizard/data/*.txt,erpnext.gettext.extractors.lines_from_txt_file.extract
1 **/setup/setup_wizard/data/uom_data.json erpnext.gettext.extractors.uom_data.extract
2 **/setup/doctype/incoterm/incoterms.csv erpnext.gettext.extractors.incoterms.extract
3 **/setup/setup_wizard/data/*.txt erpnext.gettext.extractors.lines_from_txt_file.extract

View File

@@ -1,13 +1,25 @@
module.exports = { module.exports = {
parserPreset: "conventional-changelog-conventionalcommits", parserPreset: 'conventional-changelog-conventionalcommits',
rules: { rules: {
"subject-empty": [2, "never"], 'subject-empty': [2, 'never'],
"type-case": [2, "always", "lower-case"], 'type-case': [2, 'always', 'lower-case'],
"type-empty": [2, "never"], 'type-empty': [2, 'never'],
"type-enum": [ 'type-enum': [
2, 2,
"always", 'always',
["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"], [
'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test',
],
], ],
}, },
}; };

View File

@@ -1,10 +0,0 @@
files:
- source: /erpnext/locale/main.pot
translation: /erpnext/locale/%two_letters_code%.po
pull_request_title: "fix: sync translations from crowdin"
pull_request_labels:
- translation
pull_request_reviewers:
- barredterra # change to your GitHub username if you copied this file
commit_message: "fix: %language% translations"
append_commit_message: false

View File

@@ -1,12 +1,9 @@
import functools import functools
import inspect import inspect
from typing import TypeVar
import frappe import frappe
from frappe.model.document import Document
from frappe.utils.user import is_website_user
__version__ = "16.0.0-dev" __version__ = "15.0.0-dev"
def get_default_company(user=None): def get_default_company(user=None):
@@ -16,7 +13,7 @@ def get_default_company(user=None):
if not user: if not user:
user = frappe.session.user user = frappe.session.user
companies = get_user_default_as_list("company", user) companies = get_user_default_as_list(user, "company")
if companies: if companies:
default_company = companies[0] default_company = companies[0]
else: else:
@@ -39,8 +36,10 @@ def get_default_cost_center(company):
if not frappe.flags.company_cost_center: if not frappe.flags.company_cost_center:
frappe.flags.company_cost_center = {} frappe.flags.company_cost_center = {}
if company not in frappe.flags.company_cost_center: if not company in frappe.flags.company_cost_center:
frappe.flags.company_cost_center[company] = frappe.get_cached_value("Company", company, "cost_center") frappe.flags.company_cost_center[company] = frappe.get_cached_value(
"Company", company, "cost_center"
)
return frappe.flags.company_cost_center[company] return frappe.flags.company_cost_center[company]
@@ -48,7 +47,7 @@ def get_company_currency(company):
"""Returns the default company currency""" """Returns the default company currency"""
if not frappe.flags.company_currency: if not frappe.flags.company_currency:
frappe.flags.company_currency = {} frappe.flags.company_currency = {}
if company not in frappe.flags.company_currency: if not company in frappe.flags.company_currency:
frappe.flags.company_currency[company] = frappe.db.get_value( frappe.flags.company_currency[company] = frappe.db.get_value(
"Company", company, "default_currency", cache=True "Company", company, "default_currency", cache=True
) )
@@ -82,7 +81,7 @@ def is_perpetual_inventory_enabled(company):
if not hasattr(frappe.local, "enable_perpetual_inventory"): if not hasattr(frappe.local, "enable_perpetual_inventory"):
frappe.local.enable_perpetual_inventory = {} frappe.local.enable_perpetual_inventory = {}
if company not in frappe.local.enable_perpetual_inventory: if not company in frappe.local.enable_perpetual_inventory:
frappe.local.enable_perpetual_inventory[company] = ( frappe.local.enable_perpetual_inventory[company] = (
frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0 frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
) )
@@ -97,7 +96,7 @@ def get_default_finance_book(company=None):
if not hasattr(frappe.local, "default_finance_book"): if not hasattr(frappe.local, "default_finance_book"):
frappe.local.default_finance_book = {} frappe.local.default_finance_book = {}
if company not in frappe.local.default_finance_book: if not company in frappe.local.default_finance_book:
frappe.local.default_finance_book[company] = frappe.get_cached_value( frappe.local.default_finance_book[company] = frappe.get_cached_value(
"Company", company, "default_finance_book" "Company", company, "default_finance_book"
) )
@@ -109,7 +108,7 @@ def get_party_account_type(party_type):
if not hasattr(frappe.local, "party_account_types"): if not hasattr(frappe.local, "party_account_types"):
frappe.local.party_account_types = {} frappe.local.party_account_types = {}
if party_type not in frappe.local.party_account_types: if not party_type in frappe.local.party_account_types:
frappe.local.party_account_types[party_type] = ( frappe.local.party_account_types[party_type] = (
frappe.db.get_value("Party Type", party_type, "account_type") or "" frappe.db.get_value("Party Type", party_type, "account_type") or ""
) )
@@ -152,44 +151,3 @@ def allow_regional(fn):
return frappe.get_attr(overrides[function_path][-1])(*args, **kwargs) return frappe.get_attr(overrides[function_path][-1])(*args, **kwargs)
return caller return caller
def check_app_permission():
if frappe.session.user == "Administrator":
return True
if is_website_user():
return False
return True
T = TypeVar("T")
def normalize_ctx_input(T: type) -> callable:
"""
Normalizes the first argument (ctx) of the decorated function by:
- Converting Document objects to dictionaries
- Parsing JSON strings
- Casting the result to the specified type T
"""
def decorator(func: callable):
# conserve annotations for frappe.utils.typing_validations
@functools.wraps(func, assigned=(a for a in functools.WRAPPER_ASSIGNMENTS if a != "__annotations__"))
def wrapper(ctx: T | Document | dict | str, *args, **kwargs):
if isinstance(ctx, Document):
ctx = T(**ctx.as_dict())
elif isinstance(ctx, dict):
ctx = T(**ctx)
else:
ctx = T(**frappe.parse_json(ctx))
return func(ctx, *args, **kwargs)
# set annotations from function
wrapper.__annotations__.update({k: v for k, v in func.__annotations__.items() if k != "ctx"})
return wrapper
return decorator

View File

@@ -11,14 +11,14 @@ class ERPNextAddress(Address):
def validate(self): def validate(self):
self.validate_reference() self.validate_reference()
self.update_compnay_address() self.update_compnay_address()
super().validate() super(ERPNextAddress, self).validate()
def link_address(self): def link_address(self):
"""Link address based on owner""" """Link address based on owner"""
if self.is_your_company_address: if self.is_your_company_address:
return return
return super().link_address() return super(ERPNextAddress, self).link_address()
def update_compnay_address(self): def update_compnay_address(self):
for link in self.get("links"): for link in self.get("links"):
@@ -26,11 +26,11 @@ class ERPNextAddress(Address):
self.is_your_company_address = 1 self.is_your_company_address = 1
def validate_reference(self): def validate_reference(self):
if self.is_your_company_address and not [row for row in self.links if row.link_doctype == "Company"]: if self.is_your_company_address and not [
row for row in self.links if row.link_doctype == "Company"
]:
frappe.throw( frappe.throw(
_( _("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
"Address needs to be linked to a Company. Please add a row for Company in the Links table."
),
title=_("Company Not Linked"), title=_("Company Not Linked"),
) )

View File

@@ -1,4 +1,4 @@
frappe.provide("frappe.dashboards.chart_sources"); frappe.provide('frappe.dashboards.chart_sources');
frappe.dashboards.chart_sources["Account Balance Timeline"] = { frappe.dashboards.chart_sources["Account Balance Timeline"] = {
method: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get", method: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
@@ -9,14 +9,14 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = {
fieldtype: "Link", fieldtype: "Link",
options: "Company", options: "Company",
default: frappe.defaults.get_user_default("Company"), default: frappe.defaults.get_user_default("Company"),
reqd: 1, reqd: 1
}, },
{ {
fieldname: "account", fieldname: "account",
label: __("Account"), label: __("Account"),
fieldtype: "Link", fieldtype: "Link",
options: "Account", options: "Account",
reqd: 1, reqd: 1
}, },
], ]
}; };

View File

@@ -37,7 +37,7 @@ def get(
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json) filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
account = filters.get("account") account = filters.get("account")
filters.get("company") company = filters.get("company")
if not account and chart_name: if not account and chart_name:
frappe.throw( frappe.throw(
@@ -83,6 +83,7 @@ def build_result(account, dates, gl_entries):
# get balances in debit # get balances in debit
for entry in gl_entries: for entry in gl_entries:
# entry date is after the current pointer, so move the pointer forward # entry date is after the current pointer, so move the pointer forward
while getdate(entry.posting_date) > result[date_index][0]: while getdate(entry.posting_date) > result[date_index][0]:
date_index += 1 date_index += 1
@@ -132,6 +133,8 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
dates = [get_period_ending(from_date, timegrain)] dates = [get_period_ending(from_date, timegrain)]
while getdate(dates[-1]) < getdate(to_date): while getdate(dates[-1]) < getdate(to_date):
date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain) date = get_period_ending(
add_to_date(dates[-1], years=years, months=months, days=days), timegrain
)
dates.append(date) dates.append(date)
return dates return dates

View File

@@ -24,10 +24,14 @@ from erpnext.accounts.utils import get_account_currency
def validate_service_stop_date(doc): def validate_service_stop_date(doc):
"""Validates service_stop_date for Purchase Invoice and Sales Invoice""" """Validates service_stop_date for Purchase Invoice and Sales Invoice"""
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense" enable_check = (
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
)
old_stop_dates = {} old_stop_dates = {}
old_doc = frappe.db.get_all(f"{doc.doctype} Item", {"parent": doc.name}, ["name", "service_stop_date"]) old_doc = frappe.db.get_all(
"{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
)
for d in old_doc: for d in old_doc:
old_stop_dates[d.name] = d.service_stop_date or "" old_stop_dates[d.name] = d.service_stop_date or ""
@@ -58,14 +62,16 @@ def build_conditions(process_type, account, company):
) )
if account: if account:
conditions += f"AND {deferred_account}={frappe.db.escape(account)}" conditions += "AND %s='%s'" % (deferred_account, account)
elif company: elif company:
conditions += f"AND p.company = {frappe.db.escape(company)}" conditions += f"AND p.company = {frappe.db.escape(company)}"
return conditions return conditions
def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=""): def convert_deferred_expense_to_expense(
deferred_process, start_date=None, end_date=None, conditions=""
):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM # book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date: if not start_date:
@@ -75,14 +81,16 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
# check for the purchase invoice for which GL entries has to be done # check for the purchase invoice for which GL entries has to be done
invoices = frappe.db.sql_list( invoices = frappe.db.sql_list(
f""" """
select distinct item.parent select distinct item.parent
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
where item.service_start_date<=%s and item.service_end_date>=%s where item.service_start_date<=%s and item.service_end_date>=%s
and item.enable_deferred_expense = 1 and item.parent=p.name and item.enable_deferred_expense = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0 and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{conditions} {0}
""", """.format(
conditions
),
(end_date, start_date), (end_date, start_date),
) # nosec ) # nosec
@@ -95,7 +103,9 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
send_mail(deferred_process) send_mail(deferred_process)
def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=""): def convert_deferred_revenue_to_income(
deferred_process, start_date=None, end_date=None, conditions=""
):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM # book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date: if not start_date:
@@ -105,14 +115,16 @@ def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_da
# check for the sales invoice for which GL entries has to be done # check for the sales invoice for which GL entries has to be done
invoices = frappe.db.sql_list( invoices = frappe.db.sql_list(
f""" """
select distinct item.parent select distinct item.parent
from `tabSales Invoice Item` item, `tabSales Invoice` p from `tabSales Invoice Item` item, `tabSales Invoice` p
where item.service_start_date<=%s and item.service_end_date>=%s where item.service_start_date<=%s and item.service_end_date>=%s
and item.enable_deferred_revenue = 1 and item.parent=p.name and item.enable_deferred_revenue = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0 and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{conditions} {0}
""", """.format(
conditions
),
(end_date, start_date), (end_date, start_date),
) # nosec ) # nosec
@@ -220,7 +232,7 @@ def calculate_monthly_amount(
if amount + already_booked_amount_in_account_currency > item.net_amount: if amount + already_booked_amount_in_account_currency > item.net_amount:
amount = item.net_amount - already_booked_amount_in_account_currency amount = item.net_amount - already_booked_amount_in_account_currency
if get_first_day(start_date) != start_date or get_last_day(end_date) != end_date: if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
partial_month = flt(date_diff(end_date, start_date)) / flt( partial_month = flt(date_diff(end_date, start_date)) / flt(
date_diff(get_last_day(end_date), get_first_day(start_date)) date_diff(get_last_day(end_date), get_first_day(start_date))
) )
@@ -231,7 +243,9 @@ def calculate_monthly_amount(
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount( already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
doc, item doc, item
) )
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) base_amount = flt(
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
)
if account_currency == doc.company_currency: if account_currency == doc.company_currency:
amount = base_amount amount = base_amount
else: else:
@@ -251,13 +265,17 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
if account_currency == doc.company_currency: if account_currency == doc.company_currency:
amount = base_amount amount = base_amount
else: else:
amount = flt(item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")) amount = flt(
item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")
)
else: else:
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount( already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
doc, item doc, item
) )
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) base_amount = flt(
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
)
if account_currency == doc.company_currency: if account_currency == doc.company_currency:
amount = base_amount amount = base_amount
else: else:
@@ -278,22 +296,26 @@ def get_already_booked_amount(doc, item):
gl_entries_details = frappe.db.sql( gl_entries_details = frappe.db.sql(
""" """
select sum({}) as total_credit, sum({}) as total_credit_in_account_currency, voucher_detail_no select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0 and is_cancelled = 0
group by voucher_detail_no group by voucher_detail_no
""".format(total_credit_debit, total_credit_debit_currency), """.format(
total_credit_debit, total_credit_debit_currency
),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True, as_dict=True,
) )
journal_entry_details = frappe.db.sql( journal_entry_details = frappe.db.sql(
""" """
SELECT sum(c.{}) as total_credit, sum(c.{}) as total_credit_in_account_currency, reference_detail_no SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
and p.docstatus < 2 group by reference_detail_no and p.docstatus < 2 group by reference_detail_no
""".format(total_credit_debit, total_credit_debit_currency), """.format(
total_credit_debit, total_credit_debit_currency
),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True, as_dict=True,
) )
@@ -315,7 +337,9 @@ def get_already_booked_amount(doc, item):
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense" enable_check = (
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
)
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
@@ -360,45 +384,45 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
) )
if not amount: if not amount:
prev_posting_date = end_date return
else:
gl_posting_date = end_date
prev_posting_date = None
# check if books nor frozen till endate:
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1))
prev_posting_date = end_date
if via_journal_entry: gl_posting_date = end_date
book_revenue_via_journal_entry( prev_posting_date = None
doc, # check if books nor frozen till endate:
credit_account, if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
debit_account, gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1))
amount, prev_posting_date = end_date
base_amount,
gl_posting_date, if via_journal_entry:
project, book_revenue_via_journal_entry(
account_currency, doc,
item.cost_center, credit_account,
item, debit_account,
deferred_process, amount,
submit_journal_entry, base_amount,
) gl_posting_date,
else: project,
make_gl_entries( account_currency,
doc, item.cost_center,
credit_account, item,
debit_account, deferred_process,
against, submit_journal_entry,
amount, )
base_amount, else:
gl_posting_date, make_gl_entries(
project, doc,
account_currency, credit_account,
item.cost_center, debit_account,
item, against,
deferred_process, amount,
) base_amount,
gl_posting_date,
project,
account_currency,
item.cost_center,
item,
deferred_process,
)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors # Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error: if frappe.flags.deferred_accounting_error:
@@ -416,7 +440,9 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
via_journal_entry = cint( via_journal_entry = cint(
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry") frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
) )
submit_journal_entry = cint(frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")) submit_journal_entry = cint(
frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")
)
book_deferred_entries_based_on = frappe.db.get_singles_value( book_deferred_entries_based_on = frappe.db.get_singles_value(
"Accounts Settings", "book_deferred_entries_based_on" "Accounts Settings", "book_deferred_entries_based_on"
) )
@@ -436,7 +462,9 @@ def process_deferred_accounting(posting_date=None):
posting_date = today() posting_date = today()
if not cint( if not cint(
frappe.db.get_singles_value("Accounts Settings", "automatically_process_deferred_accounting_entry") frappe.db.get_singles_value(
"Accounts Settings", "automatically_process_deferred_accounting_entry"
)
): ):
return return
@@ -559,13 +587,16 @@ def book_revenue_via_journal_entry(
deferred_process=None, deferred_process=None,
submit="No", submit="No",
): ):
if amount == 0: if amount == 0:
return return
journal_entry = frappe.new_doc("Journal Entry") journal_entry = frappe.new_doc("Journal Entry")
journal_entry.posting_date = posting_date journal_entry.posting_date = posting_date
journal_entry.company = doc.company journal_entry.company = doc.company
journal_entry.voucher_type = "Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense" journal_entry.voucher_type = (
"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
)
journal_entry.process_deferred_accounting = deferred_process journal_entry.process_deferred_accounting = deferred_process
debit_entry = { debit_entry = {
@@ -614,6 +645,7 @@ def book_revenue_via_journal_entry(
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr): def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
if doctype == "Sales Invoice": if doctype == "Sales Invoice":
credit_account, debit_account = frappe.db.get_value( credit_account, debit_account = frappe.db.get_value(
"Sales Invoice Item", "Sales Invoice Item",

View File

@@ -22,17 +22,23 @@ frappe.ui.form.on("Account", {
// hide fields if group // hide fields if group
frm.toggle_display(["tax_rate"], cint(frm.doc.is_group) == 0); frm.toggle_display(["tax_rate"], cint(frm.doc.is_group) == 0);
frm.toggle_enable(["is_group", "company", "account_number"], frm.is_new()); // disable fields
frm.toggle_enable(["is_group", "company"], false);
if (cint(frm.doc.is_group) == 0) { if (cint(frm.doc.is_group) == 0) {
frm.toggle_display("freeze_account", frm.doc.__onload && frm.doc.__onload.can_freeze_account); frm.toggle_display(
"freeze_account",
frm.doc.__onload && frm.doc.__onload.can_freeze_account
);
} }
// read-only for root accounts // read-only for root accounts
if (!frm.is_new()) { if (!frm.is_new()) {
if (!frm.doc.parent_account) { if (!frm.doc.parent_account) {
frm.set_read_only(); frm.set_read_only();
frm.set_intro(__("This is a root account and cannot be edited.")); frm.set_intro(
__("This is a root account and cannot be edited.")
);
} else { } else {
// credit days and type if customer or supplier // credit days and type if customer or supplier
frm.set_intro(null); frm.set_intro(null);
@@ -74,33 +80,27 @@ frappe.ui.form.on("Account", {
); );
if (frm.doc.is_group == 1) { if (frm.doc.is_group == 1) {
frm.add_custom_button( frm.add_custom_button(__('Convert to Non-Group'), function () {
__("Convert to Non-Group"), return frappe.call({
function () { doc: frm.doc,
return frappe.call({ method: 'convert_group_to_ledger',
doc: frm.doc, callback: function() {
method: "convert_group_to_ledger", frm.refresh();
callback: function () { }
frm.refresh(); });
}, }, __('Actions'));
});
}, } else if (cint(frm.doc.is_group) == 0
__("Actions") && frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
); frm.add_custom_button(__('General Ledger'), function () {
} else if (cint(frm.doc.is_group) == 0 && frappe.boot.user.can_read.indexOf("GL Entry") !== -1) { frappe.route_options = {
frm.add_custom_button( "account": frm.doc.name,
__("General Ledger"), "from_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
function () { "to_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
frappe.route_options = { "company": frm.doc.company
account: frm.doc.name, };
from_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], frappe.set_route("query-report", "General Ledger");
to_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, __('View'));
company: frm.doc.company,
};
frappe.set_route("query-report", "General Ledger");
},
__("View")
);
frm.add_custom_button( frm.add_custom_button(
__("Convert to Group"), __("Convert to Group"),
@@ -193,8 +193,14 @@ frappe.ui.form.on("Account", {
if (r.message) { if (r.message) {
frappe.set_route("Form", "Account", r.message); frappe.set_route("Form", "Account", r.message);
} else { } else {
frm.set_value("account_number", data.account_number); frm.set_value(
frm.set_value("account_name", data.account_name); "account_number",
data.account_number
);
frm.set_value(
"account_name",
data.account_name
);
} }
d.hide(); d.hide();
} }

View File

@@ -55,7 +55,8 @@
"fieldtype": "Data", "fieldtype": "Data",
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Account Number" "label": "Account Number",
"read_only": 1
}, },
{ {
"default": "0", "default": "0",
@@ -64,8 +65,6 @@
"label": "Is Group" "label": "Is Group"
}, },
{ {
"fetch_from": "parent_account.company",
"fetch_if_empty": 1,
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"in_standard_filter": 1, "in_standard_filter": 1,
@@ -73,6 +72,7 @@
"oldfieldname": "company", "oldfieldname": "company",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Company", "options": "Company",
"read_only": 1,
"remember_last_selected_value": 1, "remember_last_selected_value": 1,
"reqd": 1 "reqd": 1
}, },
@@ -108,7 +108,6 @@
"fieldname": "parent_account", "fieldname": "parent_account",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"in_preview": 1,
"label": "Parent Account", "label": "Parent Account",
"oldfieldname": "parent_account", "oldfieldname": "parent_account",
"oldfieldtype": "Link", "oldfieldtype": "Link",
@@ -124,8 +123,7 @@
"label": "Account Type", "label": "Account Type",
"oldfieldname": "account_type", "oldfieldname": "account_type",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary"
"search_index": 1
}, },
{ {
"description": "Rate at which this tax is applied", "description": "Rate at which this tax is applied",
@@ -194,7 +192,7 @@
"idx": 1, "idx": 1,
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2024-08-19 15:19:11.095045", "modified": "2023-07-20 18:18:44.405723",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account", "name": "Account",
@@ -251,8 +249,7 @@
], ],
"search_fields": "account_number", "search_fields": "account_number",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"show_preview_popup": 1, "sort_field": "modified",
"sort_field": "creation",
"sort_order": "ASC", "sort_order": "ASC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1

View File

@@ -23,76 +23,18 @@ class InvalidAccountMergeError(frappe.ValidationError):
class Account(NestedSet): class Account(NestedSet):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
account_currency: DF.Link | None
account_name: DF.Data
account_number: DF.Data | None
account_type: DF.Literal[
"",
"Accumulated Depreciation",
"Asset Received But Not Billed",
"Bank",
"Cash",
"Chargeable",
"Capital Work in Progress",
"Cost of Goods Sold",
"Current Asset",
"Current Liability",
"Depreciation",
"Direct Expense",
"Direct Income",
"Equity",
"Expense Account",
"Expenses Included In Asset Valuation",
"Expenses Included In Valuation",
"Fixed Asset",
"Income Account",
"Indirect Expense",
"Indirect Income",
"Liability",
"Payable",
"Receivable",
"Round Off",
"Round Off for Opening",
"Stock",
"Stock Adjustment",
"Stock Received But Not Billed",
"Service Received But Not Billed",
"Tax",
"Temporary",
]
balance_must_be: DF.Literal["", "Debit", "Credit"]
company: DF.Link
disabled: DF.Check
freeze_account: DF.Literal["No", "Yes"]
include_in_gross: DF.Check
is_group: DF.Check
lft: DF.Int
old_parent: DF.Data | None
parent_account: DF.Link
report_type: DF.Literal["", "Balance Sheet", "Profit and Loss"]
rgt: DF.Int
root_type: DF.Literal["", "Asset", "Liability", "Income", "Expense", "Equity"]
tax_rate: DF.Float
# end: auto-generated types
nsm_parent_field = "parent_account" nsm_parent_field = "parent_account"
def on_update(self): def on_update(self):
if frappe.local.flags.ignore_update_nsm: if frappe.local.flags.ignore_update_nsm:
return return
else: else:
super().on_update() super(Account, self).on_update()
def onload(self): def onload(self):
frozen_accounts_modifier = frappe.db.get_single_value("Accounts Settings", "frozen_accounts_modifier") frozen_accounts_modifier = frappe.db.get_value(
"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
)
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles(): if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
self.set_onload("can_freeze_account", True) self.set_onload("can_freeze_account", True)
@@ -102,12 +44,14 @@ class Account(NestedSet):
self.name = get_autoname_with_number(self.account_number, self.account_name, 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
if frappe.local.flags.allow_unverified_charts: if frappe.local.flags.allow_unverified_charts:
return return
self.validate_parent() self.validate_parent()
self.validate_parent_child_account_type() self.validate_parent_child_account_type()
self.validate_root_details() self.validate_root_details()
self.validate_account_number() validate_field_number("Account", self.name, self.account_number, self.company, "account_number")
self.validate_group_or_ledger() self.validate_group_or_ledger()
self.set_root_and_report_type() self.set_root_and_report_type()
self.validate_mandatory() self.validate_mandatory()
@@ -115,7 +59,6 @@ class Account(NestedSet):
self.validate_balance_must_be_debit_or_credit() self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency() self.validate_account_currency()
self.validate_root_company_and_sync_account_to_children() self.validate_root_company_and_sync_account_to_children()
self.validate_receivable_payable_account_type()
def validate_parent_child_account_type(self): def validate_parent_child_account_type(self):
if self.parent_account: if self.parent_account:
@@ -186,24 +129,6 @@ class Account(NestedSet):
"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss" "Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
) )
def validate_receivable_payable_account_type(self):
doc_before_save = self.get_doc_before_save()
receivable_payable_types = ["Receivable", "Payable"]
if (
doc_before_save
and doc_before_save.account_type in receivable_payable_types
and doc_before_save.account_type != self.account_type
):
# check for ledger entries
if frappe.db.get_all("GL Entry", filters={"account": self.name, "is_cancelled": 0}, limit=1):
msg = _(
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
).format(
frappe.bold(_("Account Type")), doc_before_save.account_type, doc_before_save.account_type
)
frappe.msgprint(msg)
self.add_comment("Comment", msg)
def validate_root_details(self): def validate_root_details(self):
doc_before_save = self.get_doc_before_save() doc_before_save = self.get_doc_before_save()
@@ -215,7 +140,9 @@ class Account(NestedSet):
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
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation: if (
frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
):
return return
ancestors = get_root_company(self.company) ancestors = get_root_company(self.company)
if ancestors: if ancestors:
@@ -308,22 +235,6 @@ class Account(NestedSet):
if frappe.db.get_value("GL Entry", {"account": self.name}): if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency")) frappe.throw(_("Currency can not be changed after making entries using some other currency"))
def validate_account_number(self, account_number=None):
if not account_number:
account_number = self.account_number
if account_number:
account_with_same_number = frappe.db.get_value(
"Account",
{"account_number": account_number, "company": self.company, "name": ["!=", self.name]},
)
if account_with_same_number:
frappe.throw(
_("Account Number {0} already used in account {1}").format(
account_number, account_with_same_number
)
)
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name): def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
for company in descendants: for company in descendants:
company_bold = frappe.bold(company) company_bold = frappe.bold(company)
@@ -429,7 +340,7 @@ class Account(NestedSet):
if self.check_gle_exists(): if self.check_gle_exists():
throw(_("Account with existing transaction can not be deleted")) throw(_("Account with existing transaction can not be deleted"))
super().on_trash(True) super(Account, self).on_trash(True)
@frappe.whitelist() @frappe.whitelist()
@@ -437,8 +348,9 @@ class Account(NestedSet):
def get_parent_account(doctype, txt, searchfield, start, page_len, filters): def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql( return frappe.db.sql(
"""select name from tabAccount """select name from tabAccount
where is_group = 1 and docstatus != 2 and company = {} where is_group = 1 and docstatus != 2 and company = %s
and {} like {} order by name limit {} offset {}""".format("%s", searchfield, "%s", "%s", "%s"), and %s like %s order by name limit %s offset %s"""
% ("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, page_len, start), (filters["company"], "%%%s%%" % txt, page_len, start),
as_list=1, as_list=1,
) )
@@ -477,6 +389,19 @@ def get_account_autoname(account_number, account_name, company):
return " - ".join(parts) return " - ".join(parts)
def validate_account_number(name, account_number, company):
if account_number:
account_with_same_number = frappe.db.get_value(
"Account", {"account_number": account_number, "company": company, "name": ["!=", name]}
)
if account_with_same_number:
frappe.throw(
_("Account Number {0} already used in account {1}").format(
account_number, account_with_same_number
)
)
@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.get_cached_doc("Account", name) account = frappe.get_cached_doc("Account", name)
@@ -517,7 +442,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
frappe.throw(message, title=_("Rename Not Allowed")) frappe.throw(message, title=_("Rename Not Allowed"))
account.validate_account_number(account_number) validate_account_number(name, account_number, account.company)
if account_number: if account_number:
frappe.db.set_value("Account", name, "account_number", account_number.strip()) frappe.db.set_value("Account", name, "account_number", account_number.strip())
else: else:
@@ -591,5 +516,7 @@ def sync_update_account_number_in_child(
if old_acc_number: if old_acc_number:
filters["account_number"] = old_acc_number filters["account_number"] = old_acc_number
for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True): for d in frappe.db.get_values(
"Account", filters=filters, fieldname=["company", "name"], as_dict=True
):
update_account_number(d["name"], account_name, account_number, from_descendant=True) update_account_number(d["name"], account_name, account_number, from_descendant=True)

View File

@@ -1,4 +1,4 @@
frappe.provide("frappe.treeview_settings"); frappe.provide("frappe.treeview_settings")
frappe.treeview_settings["Account"] = { frappe.treeview_settings["Account"] = {
breadcrumb: "Accounts", breadcrumb: "Accounts",
@@ -7,12 +7,12 @@ frappe.treeview_settings["Account"] = {
filters: [ filters: [
{ {
fieldname: "company", fieldname: "company",
fieldtype: "Select", fieldtype:"Select",
options: erpnext.utils.get_tree_options("company"), options: erpnext.utils.get_tree_options("company"),
label: __("Company"), label: __("Company"),
default: erpnext.utils.get_tree_default("company"), default: erpnext.utils.get_tree_default("company"),
on_change: function () { on_change: function() {
var me = frappe.treeview_settings["Account"].treeview; var me = frappe.treeview_settings['Account'].treeview;
var company = me.page.fields_dict.company.get_value(); var company = me.page.fields_dict.company.get_value();
if (!company) { if (!company) {
frappe.throw(__("Please set a Company")); frappe.throw(__("Please set a Company"));
@@ -22,36 +22,30 @@ frappe.treeview_settings["Account"] = {
args: { args: {
company: company, company: company,
}, },
callback: function (r) { callback: function(r) {
if (r.message) { if(r.message) {
let root_company = r.message.length ? r.message[0] : ""; let root_company = r.message.length ? r.message[0] : "";
me.page.fields_dict.root_company.set_value(root_company); me.page.fields_dict.root_company.set_value(root_company);
frappe.db.get_value( frappe.db.get_value("Company", {"name": company}, "allow_account_creation_against_child_company", (r) => {
"Company", frappe.flags.ignore_root_company_validation = r.allow_account_creation_against_child_company;
{ name: company }, });
"allow_account_creation_against_child_company",
(r) => {
frappe.flags.ignore_root_company_validation =
r.allow_account_creation_against_child_company;
}
);
} }
}, }
}); });
}, }
}, },
{ {
fieldname: "root_company", fieldname: "root_company",
fieldtype: "Data", fieldtype:"Data",
label: __("Root Company"), label: __("Root Company"),
hidden: true, hidden: true,
disable_onchange: true, disable_onchange: true
}, }
], ],
root_label: "Accounts", root_label: "Accounts",
get_tree_nodes: "erpnext.accounts.utils.get_children", get_tree_nodes: 'erpnext.accounts.utils.get_children',
on_get_node: function (nodes, deep = false) { on_get_node: function(nodes, deep=false) {
if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return; if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return;
let accounts = []; let accounts = [];
@@ -63,231 +57,151 @@ frappe.treeview_settings["Account"] = {
} }
frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => { frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => {
if (value) { if(value) {
const get_balances = frappe.call({ const get_balances = frappe.call({
method: "erpnext.accounts.utils.get_account_balances", method: 'erpnext.accounts.utils.get_account_balances',
args: { args: {
accounts: accounts, accounts: accounts,
company: cur_tree.args.company, company: cur_tree.args.company
}, },
}); });
get_balances.then((r) => { get_balances.then(r => {
if (!r.message || r.message.length == 0) return; if (!r.message || r.message.length == 0) return;
for (let account of r.message) { for (let account of r.message) {
const node = cur_tree.nodes && cur_tree.nodes[account.value]; const node = cur_tree.nodes && cur_tree.nodes[account.value];
if (!node || node.is_root) continue; if (!node || node.is_root) continue;
// show Dr if positive since balance is calculated as debit - credit else show Cr // show Dr if positive since balance is calculated as debit - credit else show Cr
const balance = account.balance_in_account_currency || account.balance; const balance = account.balance_in_account_currency || account.balance;
const dr_or_cr = balance > 0 ? __("Dr") : __("Cr"); const dr_or_cr = balance > 0 ? "Dr": "Cr";
const format = (value, currency) => format_currency(Math.abs(value), currency); const format = (value, currency) => format_currency(Math.abs(value), currency);
if (account.balance !== undefined) { if (account.balance!==undefined) {
node.parent && node.parent.find(".balance-area").remove(); node.parent && node.parent.find('.balance-area').remove();
$( $('<span class="balance-area pull-right">'
'<span class="balance-area pull-right">' + + (account.balance_in_account_currency ?
(account.balance_in_account_currency (format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
? format( + format(account.balance, account.company_currency)
account.balance_in_account_currency, + " " + dr_or_cr
account.account_currency + '</span>').insertBefore(node.$ul);
) + " / "
: "") +
format(account.balance, account.company_currency) +
" " +
dr_or_cr +
"</span>"
).insertBefore(node.$ul);
} }
} }
}); });
} }
}); });
}, },
add_tree_node: "erpnext.accounts.utils.add_ac", add_tree_node: 'erpnext.accounts.utils.add_ac',
menu_items: [ menu_items:[
{ {
label: __("New Company"), label: __('New Company'),
action: function () { action: function() { frappe.new_doc("Company", true) },
frappe.new_doc("Company", true); condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1'
}, }
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1',
},
], ],
fields: [ fields: [
{ {fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true,
fieldtype: "Data", description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")},
fieldname: "account_name", {fieldtype:'Data', fieldname:'account_number', label:__('Account Number'),
label: __("New Account Name"), description: __("Number of new Account, it will be included in the account name as a prefix")},
reqd: true, {fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
description: __( description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
"Name of new Account. Note: Please don't create accounts for Customers and Suppliers" {fieldtype:'Select', fieldname:'root_type', label:__('Root Type'),
), options: ['Asset', 'Liability', 'Equity', 'Income', 'Expense'].join('\n'),
}, depends_on: 'eval:doc.is_group && !doc.parent_account'},
{ {fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
fieldtype: "Data", options: frappe.get_meta("Account").fields.filter(d => d.fieldname=='account_type')[0].options,
fieldname: "account_number", description: __("Optional. This setting will be used to filter in various transactions.")
label: __("Account Number"),
description: __("Number of new Account, it will be included in the account name as a prefix"),
},
{
fieldtype: "Check",
fieldname: "is_group",
label: __("Is Group"),
description: __(
"Further accounts can be made under Groups, but entries can be made against non-Groups"
),
},
{
fieldtype: "Select",
fieldname: "root_type",
label: __("Root Type"),
options: ["Asset", "Liability", "Equity", "Income", "Expense"].join("\n"),
depends_on: "eval:doc.is_group && !doc.parent_account",
},
{
fieldtype: "Select",
fieldname: "account_type",
label: __("Account Type"),
options: frappe.get_meta("Account").fields.filter((d) => d.fieldname == "account_type")[0]
.options,
description: __("Optional. This setting will be used to filter in various transactions."),
},
{
fieldtype: "Float",
fieldname: "tax_rate",
label: __("Tax Rate"),
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"',
},
{
fieldtype: "Link",
fieldname: "account_currency",
label: __("Currency"),
options: "Currency",
description: __("Optional. Sets company's default currency, if not specified."),
}, },
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'),
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'},
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
description: __("Optional. Sets company's default currency, if not specified.")}
], ],
ignore_fields: ["parent_account"], ignore_fields:["parent_account"],
onload: function (treeview) { onload: function(treeview) {
frappe.treeview_settings["Account"].treeview = {}; frappe.treeview_settings['Account'].treeview = {};
$.extend(frappe.treeview_settings["Account"].treeview, treeview); $.extend(frappe.treeview_settings['Account'].treeview, treeview);
function get_company() { function get_company() {
return treeview.page.fields_dict.company.get_value(); return treeview.page.fields_dict.company.get_value();
} }
// tools // tools
treeview.page.add_inner_button( treeview.page.add_inner_button(__("Chart of Cost Centers"), function() {
__("Chart of Cost Centers"), frappe.set_route('Tree', 'Cost Center', {company: get_company()});
function () { }, __('View'));
frappe.set_route("Tree", "Cost Center", { company: get_company() });
},
__("View")
);
treeview.page.add_inner_button( treeview.page.add_inner_button(__("Opening Invoice Creation Tool"), function() {
__("Opening Invoice Creation Tool"), frappe.set_route('Form', 'Opening Invoice Creation Tool', {company: get_company()});
function () { }, __('View'));
frappe.set_route("Form", "Opening Invoice Creation Tool", { company: get_company() });
},
__("View")
);
treeview.page.add_inner_button( treeview.page.add_inner_button(__("Period Closing Voucher"), function() {
__("Period Closing Voucher"), frappe.set_route('List', 'Period Closing Voucher', {company: get_company()});
function () { }, __('View'));
frappe.set_route("List", "Period Closing Voucher", { company: get_company() });
},
__("View")
);
treeview.page.add_inner_button(
__("Journal Entry"), treeview.page.add_inner_button(__("Journal Entry"), function() {
function () { frappe.new_doc('Journal Entry', {company: get_company()});
frappe.new_doc("Journal Entry", { company: get_company() }); }, __('Create'));
}, treeview.page.add_inner_button(__("Company"), function() {
__("Create") frappe.new_doc('Company');
); }, __('Create'));
treeview.page.add_inner_button(
__("Company"),
function () {
frappe.new_doc("Company");
},
__("Create")
);
// financial statements // financial statements
for (let report of [ for (let report of ['Trial Balance', 'General Ledger', 'Balance Sheet',
"Trial Balance", 'Profit and Loss Statement', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
"General Ledger", treeview.page.add_inner_button(__(report), function() {
"Balance Sheet", frappe.set_route('query-report', report, {company: get_company()});
"Profit and Loss Statement", }, __('Financial Statements'));
"Cash Flow",
"Accounts Payable",
"Accounts Receivable",
]) {
treeview.page.add_inner_button(
__(report),
function () {
frappe.set_route("query-report", report, { company: get_company() });
},
__("Financial Statements")
);
} }
},
post_render: function (treeview) {
frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree;
treeview.page.set_primary_action(
__("New"),
function () {
let root_company = treeview.page.fields_dict.root_company.get_value();
if (root_company) { },
frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]); post_render: function(treeview) {
} else { frappe.treeview_settings['Account'].treeview["tree"] = treeview.tree;
treeview.new_node(); treeview.page.set_primary_action(__("New"), function() {
} let root_company = treeview.page.fields_dict.root_company.get_value();
},
"add" if(root_company) {
); frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
} else {
treeview.new_node();
}
}, "add");
}, },
toolbar: [ toolbar: [
{ {
label: __("Add Child"), label:__("Add Child"),
condition: function (node) { condition: function(node) {
return ( return frappe.boot.user.can_create.indexOf("Account") !== -1
frappe.boot.user.can_create.indexOf("Account") !== -1 && && (!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value()
(!frappe.treeview_settings[ || frappe.flags.ignore_root_company_validation)
"Account" && node.expandable && !node.hide_add;
].treeview.page.fields_dict.root_company.get_value() ||
frappe.flags.ignore_root_company_validation) &&
node.expandable &&
!node.hide_add
);
}, },
click: function () { click: function() {
var me = frappe.views.trees["Account"]; var me = frappe.views.trees['Account'];
me.new_node(); me.new_node();
}, },
btnClass: "hidden-xs", btnClass: "hidden-xs"
}, },
{ {
condition: function (node) { condition: function(node) {
return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1; return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1
}, },
label: __("View Ledger"), label: __("View Ledger"),
click: function (node, btn) { click: function(node, btn) {
frappe.route_options = { frappe.route_options = {
account: node.label, "account": node.label,
from_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], "from_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
to_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], "to_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
company: "company": frappe.treeview_settings['Account'].treeview.page.fields_dict.company.get_value()
frappe.treeview_settings["Account"].treeview.page.fields_dict.company.get_value(),
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
}, },
btnClass: "hidden-xs", btnClass: "hidden-xs"
}, }
], ],
extend_toolbar: true, extend_toolbar: true
}; }

View File

@@ -31,6 +31,7 @@ def create_charts(
"tax_rate", "tax_rate",
"account_currency", "account_currency",
]: ]:
account_number = cstr(child.get("account_number")).strip() account_number = cstr(child.get("account_number")).strip()
account_name, account_name_in_db = add_suffix_if_duplicate( account_name, account_name_in_db = add_suffix_if_duplicate(
account_name, account_number, accounts account_name, account_number, accounts
@@ -38,9 +39,7 @@ def create_charts(
is_group = identify_is_group(child) is_group = identify_is_group(child)
report_type = ( report_type = (
"Balance Sheet" "Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
if root_type in ["Asset", "Liability", "Equity"]
else "Profit and Loss"
) )
account = frappe.get_doc( account = frappe.get_doc(
@@ -75,7 +74,7 @@ def create_charts(
# after all accounts are already inserted. # after all accounts are already inserted.
frappe.local.flags.ignore_update_nsm = True frappe.local.flags.ignore_update_nsm = True
_import_accounts(chart, None, None, root_account=True) _import_accounts(chart, None, None, root_account=True)
rebuild_tree("Account") rebuild_tree("Account", "parent_account")
frappe.local.flags.ignore_update_nsm = False frappe.local.flags.ignore_update_nsm = False
@@ -142,7 +141,7 @@ def get_chart(chart_template, existing_company=None):
for fname in os.listdir(path): for fname in os.listdir(path):
fname = frappe.as_unicode(fname) fname = frappe.as_unicode(fname)
if fname.endswith(".json"): if fname.endswith(".json"):
with open(os.path.join(path, fname)) as f: with open(os.path.join(path, fname), "r") as f:
chart = f.read() chart = f.read()
if chart and json.loads(chart).get("name") == chart_template: if chart and json.loads(chart).get("name") == chart_template:
return json.loads(chart).get("tree") return json.loads(chart).get("tree")
@@ -174,7 +173,7 @@ def get_charts_for_country(country, with_standard=False):
for fname in os.listdir(path): for fname in os.listdir(path):
fname = frappe.as_unicode(fname) fname = frappe.as_unicode(fname)
if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"): if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"):
with open(os.path.join(path, fname)) as f: with open(os.path.join(path, fname), "r") as f:
_get_chart_name(f.read()) _get_chart_name(f.read())
# if more than one charts, returned then add the standard # if more than one charts, returned then add the standard
@@ -232,8 +231,6 @@ def build_account_tree(tree, parent, all_accounts):
tree[child.account_name]["account_type"] = child.account_type tree[child.account_name]["account_type"] = child.account_type
if child.tax_rate: if child.tax_rate:
tree[child.account_name]["tax_rate"] = child.tax_rate tree[child.account_name]["tax_rate"] = child.tax_rate
if child.account_currency:
tree[child.account_name]["account_currency"] = child.account_currency
if not parent: if not parent:
tree[child.account_name]["root_type"] = child.root_type tree[child.account_name]["root_type"] = child.root_type
@@ -250,13 +247,7 @@ def validate_bank_account(coa, bank_account):
def _get_account_names(account_master): def _get_account_names(account_master):
for account_name, child in account_master.items(): for account_name, child in account_master.items():
if account_name not in [ if account_name not in ["account_number", "account_type", "root_type", "is_group", "tax_rate"]:
"account_number",
"account_type",
"root_type",
"is_group",
"tax_rate",
]:
accounts.append(account_name) accounts.append(account_name)
_get_account_names(child) _get_account_names(child)

View File

@@ -26,7 +26,7 @@
"0360 Bauliche Investitionen in fremden (gepachteten) Betriebs- und Geschäftsgebäuden": {"account_type": "Fixed Asset"}, "0360 Bauliche Investitionen in fremden (gepachteten) Betriebs- und Geschäftsgebäuden": {"account_type": "Fixed Asset"},
"0370 Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgebäuden": {"account_type": "Fixed Asset"}, "0370 Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgebäuden": {"account_type": "Fixed Asset"},
"0390 Kumulierte Abschreibungen zu Grundstücken ": {"account_type": "Fixed Asset"}, "0390 Kumulierte Abschreibungen zu Grundstücken ": {"account_type": "Fixed Asset"},
"0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"}, "0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"},
"0500 Maschinenwerkzeuge ": {"account_type": "Fixed Asset"}, "0500 Maschinenwerkzeuge ": {"account_type": "Fixed Asset"},
"0510 Allgemeine Werkzeuge und Handwerkzeuge ": {"account_type": "Fixed Asset"}, "0510 Allgemeine Werkzeuge und Handwerkzeuge ": {"account_type": "Fixed Asset"},
"0520 Prototypen, Formen, Modelle ": {"account_type": "Fixed Asset"}, "0520 Prototypen, Formen, Modelle ": {"account_type": "Fixed Asset"},
@@ -65,41 +65,42 @@
"0980 Geleistete Anzahlungen auf Finanzanlagen ": {"account_type": "Fixed Asset"}, "0980 Geleistete Anzahlungen auf Finanzanlagen ": {"account_type": "Fixed Asset"},
"0990 Kumulierte Abschreibungen zu Finanzanlagen ": {"account_type": "Fixed Asset"}, "0990 Kumulierte Abschreibungen zu Finanzanlagen ": {"account_type": "Fixed Asset"},
"root_type": "Asset" "root_type": "Asset"
}, },
"Klasse 1 Aktiva: Vorr\u00e4te": { "Klasse 1 Aktiva: Vorr\u00e4te": {
"1000 Bezugsverrechnung": {"account_type": "Stock"}, "1000 Bezugsverrechnung": {"account_type": "Stock"},
"1100 Rohstoffe": {"account_type": "Stock"}, "1100 Rohstoffe": {"account_type": "Stock"},
"1200 Bezogene Teile": {"account_type": "Stock"}, "1200 Bezogene Teile": {"account_type": "Stock"},
"1300 Hilfsstoffe": {"account_type": "Stock"}, "1300 Hilfsstoffe": {"account_type": "Stock"},
"1350 Betriebsstoffe": {"account_type": "Stock"}, "1350 Betriebsstoffe": {"account_type": "Stock"},
"1360 Vorrat Energietraeger": {"account_type": "Stock"}, "1360 Vorrat Energietraeger": {"account_type": "Stock"},
"1400 Unfertige Erzeugnisse": {"account_type": "Stock"}, "1400 Unfertige Erzeugnisse": {"account_type": "Stock"},
"1500 Fertige Erzeugnisse": {"account_type": "Stock"}, "1500 Fertige Erzeugnisse": {"account_type": "Stock"},
"1600 Handelswarenvorrat": {"account_type": "Stock Received But Not Billed"}, "1600 Handelswarenvorrat": {"account_type": "Stock Received But Not Billed"},
"1700 Noch nicht abrechenbare Leistungen": {"account_type": "Stock"}, "1700 Noch nicht abrechenbare Leistungen": {"account_type": "Stock"},
"1900 Wertberichtigungen": {"account_type": "Stock"},
"1800 Geleistete Anzahlungen": {"account_type": "Stock"}, "1800 Geleistete Anzahlungen": {"account_type": "Stock"},
"1900 Wertberichtigungen": {"account_type": "Stock"}, "1900 Wertberichtigungen": {"account_type": "Stock"},
"root_type": "Asset" "root_type": "Asset"
}, },
"Klasse 3 Passiva: Verbindlichkeiten": { "Klasse 3 Passiva: Verbindlichkeiten": {
"3000 Allgemeine Verbindlichkeiten (Schuld)": {"account_type": "Payable"}, "3000 Allgemeine Verbindlichkeiten (Schuld)": {"account_type": "Payable"},
"3010 R\u00fcckstellungen f\u00fcr Pensionen": {"account_type": "Payable"}, "3010 R\u00fcckstellungen f\u00fcr Pensionen": {"account_type": "Payable"},
"3020 Steuerr\u00fcckstellungen": {"account_type": "Tax"}, "3020 Steuerr\u00fcckstellungen": {"account_type": "Tax"},
"3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"}, "3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"},
"3110 Verbindlichkeiten gegen\u00fcber Bank": {"account_type": "Payable"}, "3110 Verbindlichkeiten gegen\u00fcber Bank": {"account_type": "Payable"},
"3150 Verbindlichkeiten Darlehen": {"account_type": "Payable"}, "3150 Verbindlichkeiten Darlehen": {"account_type": "Payable"},
"3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"}, "3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"},
"3380 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": { "3380 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
"account_type": "Payable" "account_type": "Payable"
}, },
"3400 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": {},
"3460 Verbindlichkeiten gegenueber Gesellschaftern": {"account_type": "Payable"}, "3460 Verbindlichkeiten gegenueber Gesellschaftern": {"account_type": "Payable"},
"3470 Einlagen stiller Gesellschafter": {"account_type": "Payable"}, "3470 Einlagen stiller Gesellschafter": {"account_type": "Payable"},
"3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"}, "3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"},
"3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"}, "3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"},
"3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"}, "3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"},
"3600 Verbindlichkeiten Sozialversicherung": {"account_type": "Payable"}, "3600 Verbindlichkeiten Sozialversicherung": {"account_type": "Payable"},
"3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"}, "3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"},
"3700 Sonstige Verbindlichkeiten": {"account_type": "Payable"}, "3700 Sonstige Verbindlichkeiten": {"account_type": "Payable"},
"3900 Passive Rechnungsabgrenzungsposten": {"account_type": "Payable"}, "3900 Passive Rechnungsabgrenzungsposten": {"account_type": "Payable"},
"3100 Anleihen (einschlie\u00dflich konvertibler)": {"account_type": "Payable"}, "3100 Anleihen (einschlie\u00dflich konvertibler)": {"account_type": "Payable"},
@@ -118,13 +119,13 @@
}, },
"3515 Umsatzsteuer Inland 10%": { "3515 Umsatzsteuer Inland 10%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"3520 Umsatzsteuer aus i.g. Erwerb 20%": { "3520 Umsatzsteuer aus i.g. Erwerb 20%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"3525 Umsatzsteuer aus i.g. Erwerb 10%": { "3525 Umsatzsteuer aus i.g. Erwerb 10%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"3560 Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {}, "3560 Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
"3360 Verbindlichkeiten aus Lieferungen u. Leistungen EU": { "3360 Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
"account_type": "Payable" "account_type": "Payable"
@@ -140,7 +141,7 @@
"account_type": "Tax" "account_type": "Tax"
}, },
"root_type": "Liability" "root_type": "Liability"
}, },
"Klasse 2 Aktiva: Umlaufverm\u00f6gen, Rechnungsabgrenzungen": { "Klasse 2 Aktiva: Umlaufverm\u00f6gen, Rechnungsabgrenzungen": {
"2030 Forderungen aus Lieferungen und Leistungen Inland (0% USt, umsatzsteuerfrei)": { "2030 Forderungen aus Lieferungen und Leistungen Inland (0% USt, umsatzsteuerfrei)": {
"account_type": "Receivable" "account_type": "Receivable"
@@ -153,7 +154,7 @@
}, },
"2040 Forderungen aus Lieferungen und Leistungen Inland (sonstiger USt-Satz)": { "2040 Forderungen aus Lieferungen und Leistungen Inland (sonstiger USt-Satz)": {
"account_type": "Receivable" "account_type": "Receivable"
}, },
"2100 Forderungen aus Lieferungen und Leistungen EU": { "2100 Forderungen aus Lieferungen und Leistungen EU": {
"account_type": "Receivable" "account_type": "Receivable"
}, },
@@ -191,7 +192,7 @@
"account_type": "Receivable" "account_type": "Receivable"
}, },
"2570 Einfuhrumsatzsteuer (bezahlt)": {"account_type": "Tax"}, "2570 Einfuhrumsatzsteuer (bezahlt)": {"account_type": "Tax"},
"2460 Eingeforderte aber noch nicht eingezahlte Einlagen": { "2460 Eingeforderte aber noch nicht eingezahlte Einlagen": {
"account_type": "Receivable" "account_type": "Receivable"
}, },
@@ -242,10 +243,10 @@
}, },
"2800 Guthaben bei Bank": { "2800 Guthaben bei Bank": {
"account_type": "Bank" "account_type": "Bank"
}, },
"2801 Guthaben bei Bank - Sparkonto": { "2801 Guthaben bei Bank - Sparkonto": {
"account_type": "Bank" "account_type": "Bank"
}, },
"2810 Guthaben bei Paypal": { "2810 Guthaben bei Paypal": {
"account_type": "Bank" "account_type": "Bank"
}, },
@@ -263,19 +264,19 @@
}, },
"2895 Schwebende Geldbewegugen": { "2895 Schwebende Geldbewegugen": {
"account_type": "Bank" "account_type": "Bank"
}, },
"2513 Vorsteuer Inland 5%": { "2513 Vorsteuer Inland 5%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"2515 Vorsteuer Inland 20%": { "2515 Vorsteuer Inland 20%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"2520 Vorsteuer aus innergemeinschaftlichem Erwerb 10%": { "2520 Vorsteuer aus innergemeinschaftlichem Erwerb 10%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"2525 Vorsteuer aus innergemeinschaftlichem Erwerb 20%": { "2525 Vorsteuer aus innergemeinschaftlichem Erwerb 20%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"2530 Vorsteuer \u00a719/Art 19 ( reverse charge ) ": { "2530 Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
"account_type": "Tax" "account_type": "Tax"
}, },
@@ -285,16 +286,16 @@
"root_type": "Asset" "root_type": "Asset"
}, },
"Klasse 4: Betriebliche Erträge": { "Klasse 4: Betriebliche Erträge": {
"4000 Erlöse 20 %": {"account_type": "Income Account"}, "4000 Erlöse 20 %": {"account_type": "Income Account"},
"4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"}, "4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"},
"4010 Erl\u00f6se 10 %": {"account_type": "Income Account"}, "4010 Erl\u00f6se 10 %": {"account_type": "Income Account"},
"4030 Erl\u00f6se 13 %": {"account_type": "Income Account"}, "4030 Erl\u00f6se 13 %": {"account_type": "Income Account"},
"4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"}, "4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"},
"4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"}, "4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"},
"4410 Erl\u00f6sreduktion 10 %": {"account_type": "Expense Account"}, "4410 Erl\u00f6sreduktion 10 %": {"account_type": "Expense Account"},
"4420 Erl\u00f6sreduktion 20 %": {"account_type": "Expense Account"}, "4420 Erl\u00f6sreduktion 20 %": {"account_type": "Expense Account"},
"4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"}, "4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"},
"4440 Erl\u00f6sreduktion 0 % innergemeinschaftliche Lieferungen": {"account_type": "Expense Account"}, "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"}, "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"}, "4580 Aktivierte Eigenleistungen": {"account_type": "Income Account"},
"4600 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"}, "4600 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
@@ -303,15 +304,15 @@
"4700 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {"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"}, "4800 \u00dcbrige betriebliche Ertr\u00e4ge": {"account_type": "Income Account"},
"root_type": "Income" "root_type": "Income"
}, },
"Klasse 5: Aufwand f\u00fcr Material und Leistungen": { "Klasse 5: Aufwand f\u00fcr Material und Leistungen": {
"5000 Einkauf Partnerleistungen": {"account_type": "Cost of Goods Sold"}, "5000 Einkauf Partnerleistungen": {"account_type": "Cost of Goods Sold"},
"5100 Verbrauch an Rohstoffen": {"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"}, "5200 Verbrauch von bezogenen Fertig- und Einzelteilen": {"account_type": "Cost of Goods Sold"},
"5300 Verbrauch von Hilfsstoffen": {"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"}, "5340 Verbrauch Verpackungsmaterial": {"account_type": "Cost of Goods Sold"},
"5470 Verbrauch von Kleinmaterial": {"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"}, "5450 Verbrauch von Reinigungsmaterial": {"account_type": "Cost of Goods Sold"},
"5400 Verbrauch von Betriebsstoffen": {"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"}, "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"}, "5600 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {"account_type": "Cost of Goods Sold"},
@@ -339,7 +340,7 @@
"6700 Sonstige Sozialaufwendungen": {"account_type": "Payable"}, "6700 Sonstige Sozialaufwendungen": {"account_type": "Payable"},
"6900 Aufwandsstellenrechnung Personal": {"account_type": "Payable"}, "6900 Aufwandsstellenrechnung Personal": {"account_type": "Payable"},
"root_type": "Expense" "root_type": "Expense"
}, },
"Klasse 7: Abschreibungen und sonstige betriebliche Aufwendungen": { "Klasse 7: Abschreibungen und sonstige betriebliche Aufwendungen": {
"7010 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {"account_type": "Depreciation"}, "7010 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {"account_type": "Depreciation"},
"7100 Sonstige Steuern und Geb\u00fchren": {"account_type": "Tax"}, "7100 Sonstige Steuern und Geb\u00fchren": {"account_type": "Tax"},
@@ -348,7 +349,7 @@
"7310 Fahrrad - Aufwand": {"account_type": "Expense Account"}, "7310 Fahrrad - Aufwand": {"account_type": "Expense Account"},
"7320 Kfz - Aufwand": {"account_type": "Expense Account"}, "7320 Kfz - Aufwand": {"account_type": "Expense Account"},
"7330 LKW - Aufwand": {"account_type": "Expense Account"}, "7330 LKW - Aufwand": {"account_type": "Expense Account"},
"7340 Lastenrad - Aufwand": {"account_type": "Expense Account"}, "7340 Lastenrad - Aufwand": {"account_type": "Expense Account"},
"7350 Reise- und Fahraufwand": {"account_type": "Expense Account"}, "7350 Reise- und Fahraufwand": {"account_type": "Expense Account"},
"7360 Tag- und N\u00e4chtigungsgelder": {"account_type": "Expense Account"}, "7360 Tag- und N\u00e4chtigungsgelder": {"account_type": "Expense Account"},
"7380 Nachrichtenaufwand": {"account_type": "Expense Account"}, "7380 Nachrichtenaufwand": {"account_type": "Expense Account"},
@@ -408,7 +409,7 @@
"8990 Gewinnabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {"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"}, "8350 nicht ausgenutzte Lieferantenskonti": {"account_type": "Expense Account"},
"root_type": "Income" "root_type": "Income"
}, },
"Klasse 9 Passiva: Eigenkapital, R\u00fccklagen, stille Einlagen, Abschlusskonten": { "Klasse 9 Passiva: Eigenkapital, R\u00fccklagen, stille Einlagen, Abschlusskonten": {
"9000 Gezeichnetes bzw. gewidmetes Kapital": { "9000 Gezeichnetes bzw. gewidmetes Kapital": {
"account_type": "Equity" "account_type": "Equity"
@@ -434,5 +435,5 @@
}, },
"root_type": "Equity" "root_type": "Equity"
} }
} }
} }

View File

@@ -0,0 +1,531 @@
{
"country_code": "tr",
"name": "Turkey - Tek D\u00fczen Hesap Plan\u0131",
"tree": {
"Duran Varl\u0131klar": {
"Di\u011fer Alacaklar": {
"Ba\u011fl\u0131 Ortakl\u0131klardan Alacaklar": {},
"Di\u011fer Alacak Senetleri Reeskontu(-)": {},
"Di\u011fer \u00c7e\u015fitli Alacaklar": {},
"Ortaklardan Alacaklar": {},
"Personelden Alacaklar": {},
"\u0130\u015ftiraklerden Alacaklar": {},
"\u015e\u00fcpheli Di\u011fer Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
},
"Di\u011fer Duran Varl\u0131klar": {
"Birikmi\u015f Amortismanlar(-)": {},
"Di\u011fer KDV": {},
"Di\u011fer \u00c7e\u015fitli Duran Varl\u0131klar": {},
"Elden \u00c7\u0131kar\u0131lacak Stoklar Ve Maddi Duran Varl\u0131klar": {},
"Gelecek Y\u0131llar \u0130htiyac\u0131 Stoklar": {},
"Gelecek Y\u0131llarda \u0130ndirilecek KDV": {},
"Pe\u015fin \u00d6denen Vergi Ve Fonlar": {},
"Stok De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
},
"Gelecek Y\u0131llara Ait Giderler ve Gelir Tahakkuklar\u0131": {
"Gelecek Y\u0131llara Ait Giderler": {},
"Gelir Tahakkuklar\u0131": {}
},
"Maddi Duran Varl\u0131klar": {
"Arazi Ve Arsalar": {},
"Binalar": {},
"Birikmi\u015f Amortismanlar(-)": {},
"Demirba\u015flar": {},
"Di\u011fer Maddi Duran Varl\u0131klar": {},
"Ta\u015f\u0131tlar": {},
"Tesis, Makine Ve Cihazlar": {},
"Verilen Avanslar": {},
"Yap\u0131lmakta Olan Yat\u0131r\u0131mlar": {},
"Yer Alt\u0131 Ve Yer \u00dcst\u00fc D\u00fczenleri": {}
},
"Maddi Olmayan Duran Varl\u0131klar": {
"Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
"Birikmi\u015f Amortismanlar(-)": {},
"Di\u011fer Maddi Olmayan Duran Varl\u0131klar": {},
"Haklar": {},
"Kurulu\u015f Ve \u00d6rg\u00fctlenme Giderleri": {},
"Verilen Avanslar": {},
"\u00d6zel Maliyetler": {},
"\u015eerefiye": {}
},
"Mali Duran Varl\u0131klar": {
"Ba\u011fl\u0131 Menkul K\u0131ymetler": {},
"Ba\u011fl\u0131 Menkul K\u0131ymetler De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
"Ba\u011fl\u0131 Ortakl\u0131klar": {},
"Ba\u011fl\u0131 Ortakl\u0131klar Sermaye Paylar\u0131 De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
"Ba\u011fl\u0131 Ortakl\u0131klara Sermaye Taahh\u00fctleri(-)": {},
"Di\u011fer Mali Duran Varl\u0131klar": {},
"Di\u011fer Mali Duran Varl\u0131klar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
"\u0130\u015ftirakler": {},
"\u0130\u015ftirakler Sermaye Paylar\u0131 De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
"\u0130\u015ftiraklere Sermaye Taahh\u00fctleri(-)": {}
},
"Ticari Alacaklar": {
"Alacak Senetleri": {},
"Alacak Senetleri Reeskontu(-)": {},
"Al\u0131c\u0131lar": {},
"Kazaqn\u0131lmam\u0131\u015f Finansal Kiralama Faiz Gelirleri(-)": {},
"Verilen Depozito Ve Teminatlar": {},
"\u015e\u00fcpheli Ticari Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
},
"root_type": "",
"\u00d6zel T\u00fckenmeye Tabi Varl\u0131klar": {
"Arama Giderleri": {},
"Birikmi\u015f T\u00fckenme Paylar\u0131(-)": {},
"Di\u011fer \u00d6zel T\u00fckenmeye Tabi Varl\u0131klar": {},
"Haz\u0131rl\u0131k Ve Geli\u015ftirme Giderleri": {},
"Verilen Avanslar": {}
}
},
"D\u00f6nen Varl\u0131klar": {
"Di\u011fer Alacaklar": {
"Ba\u011fl\u0131 Ortakl\u0131klardan Alacaklar": {},
"Di\u011fer Alacak Senetleri Reeskontu(-)": {},
"Di\u011fer \u00c7e\u015fitli Alacaklar": {},
"Ortaklardan Alacaklar": {},
"Personelden Alacaklar": {},
"\u0130\u015ftiraklerden Alacaklar": {},
"\u015e\u00fcpheli Di\u011fer Alacaklar": {},
"\u015e\u00fcpheli Di\u011fer Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
},
"Di\u011fer D\u00f6nen Varl\u0131klar": {
"Devreden KDV": {},
"Di\u011fer D\u00f6nen Varl\u0131klar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
"Di\u011fer KDV": {},
"Di\u011fer \u00c7e\u015fitli D\u00f6nen Varl\u0131klar": {},
"Personel Avanslar\u0131": {},
"Pe\u015fin \u00d6denen Vergiler Ve Fonlar": {},
"Say\u0131m Ve Tesell\u00fcm Noksanlar\u0131": {},
"\u0130ndirilecek KDV": {},
"\u0130\u015f Avanslar\u0131": {}
},
"Gelecek Aylara Ait Giderler ve Gelir Tahakkuklar\u0131": {
"Gelecek Aylara Ait Giderler": {},
"Gelir Tahakkuklar\u0131": {}
},
"Haz\u0131r De\u011ferler": {
"Al\u0131nan \u00c7ekler": {},
"Bankalar": {
"account_type": "Bank"
},
"Di\u011fer Haz\u0131r De\u011ferler": {},
"Kasa": {
"account_type": "Cash"
},
"Verilen \u00c7ekler ve \u00d6deme Emirleri(-)": {}
},
"Menkul K\u0131ymetler": {
"Di\u011fer Menkul K\u0131ymetler": {},
"Hisse Senetleri": {},
"Kamu Kesimi Tahvil, Senet ve Bonolar\u0131": {},
"Menkul K\u0131ymetler De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
"\u00d6zel Kesim Tahvil Senet Ve Bonolar\u0131": {}
},
"Stoklar": {
"Mamuller": {},
"Stok De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
"Ticari Mallar": {},
"Verilen Sipari\u015f Avanslar\u0131": {},
"Yar\u0131 Mamuller": {},
"\u0130lk Madde Malzeme": {}
},
"Ticari Alacaklar": {
"Alacak Senetleri": {},
"Alacak Senetleri Reeskontu(-)": {},
"Al\u0131c\u0131lar": {},
"Di\u011fer Ticari Alacaklar": {},
"Kazan\u0131lmam\u0131\u015f Finansal Kiralama Faiz Gelirleri(-)": {},
"Verilen Depozito ve Teminatlar": {},
"\u015e\u00fcpheli Ticari Alacaklar": {},
"\u015e\u00fcpheli Ticari Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131": {}
},
"Y\u0131llara Yayg\u0131n \u0130n\u015faat ve Onar\u0131m Maliyetleri": {
"Ta\u015feronlara Verilen Avanslar": {},
"Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Maliyetleri": {}
},
"root_type": ""
},
"Gelir Tablosu Hesaplar\u0131": {
"Br\u00fct Sat\u0131\u015flar": {
"Di\u011fer Gelirler": {},
"Yurt D\u0131\u015f\u0131 Sat\u0131\u015flar": {},
"Yurt \u0130\u00e7i Sat\u0131\u015flar": {}
},
"Di\u011fer Faaliyetlerden Olu\u015fan Gelir ve K\u00e2rlar": {
"Ba\u011fl\u0131 Ortakl\u0131klardan Temett\u00fc Gelirleri": {},
"Di\u011fer Ola\u011fan Gelir Ve K\u00e2rlar": {},
"Enflasyon D\u00fczeltme K\u00e2rlar\u0131": {},
"Faiz Gelirleri": {},
"Kambiyo K\u00e2rlar\u0131": {},
"Komisyon Gelirleri": {},
"Konusu Kalmayan Kar\u015f\u0131l\u0131klar": {},
"Menkul K\u0131ymet Sat\u0131\u015f K\u00e2rlar\u0131": {},
"Reeskont Faiz Gelirleri": {},
"\u0130\u015ftiraklerden Temett\u00fc Gelirleri": {}
},
"Di\u011fer Faaliyetlerden Olu\u015fan Gider ve Zararlar (-)": {
"Di\u011fer Ola\u011fan Gider Ve Zararlar(-)": {},
"Enflasyon D\u00fczeltmesi Zararlar\u0131(-)": {},
"Kambiyo Zararlar\u0131(-)": {},
"Kar\u015f\u0131l\u0131k Giderleri(-)": {},
"Komisyon Giderleri(-)": {},
"Menkul K\u0131ymet Sat\u0131\u015f Zararlar\u0131(-)": {},
"Reeskont Faiz Giderleri(-)": {}
},
"D\u00f6nem Net K\u00e2r\u0131 Ve Zarar\u0131": {
"D\u00f6nem K\u00e2r\u0131 Vergi Ve Di\u011fer Yasal Y\u00fck\u00fcml\u00fcl\u00fck Kar\u015f\u0131l\u0131klar\u0131(-)": {},
"D\u00f6nem K\u00e2r\u0131 Veya Zarar\u0131": {},
"D\u00f6nem Net K\u00e2r\u0131 Veya Zarar\u0131": {},
"Enflasyon D\u00fczeltme Hesab\u0131": {},
"Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Enflasyon D\u00fczeltme Hesab\u0131": {}
},
"Faaliyet Giderleri(-)": {
"Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri(-)": {},
"Genel Y\u00f6netim Giderleri(-)": {},
"Pazarlama Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri(-)": {}
},
"Finansman Giderleri": {
"K\u0131sa Vadeli Bor\u00e7lanma Giderleri(-)": {},
"Uzun Vadeli Bor\u00e7lanma Giderleri(-)": {}
},
"Ola\u011fan D\u0131\u015f\u0131 Gelir Ve K\u00e2rlar": {
"Di\u011fer Ola\u011fan D\u0131\u015f\u0131 Gelir Ve K\u00e2rlar": {},
"\u00d6nceki D\u00f6nem Gelir Ve K\u00e2rlar\u0131": {}
},
"Ola\u011fan D\u0131\u015f\u0131 Gider Ve Zaralar(-)": {
"Di\u011fer Ola\u011fan D\u0131\u015f\u0131 Gider Ve Zararlar(-)": {},
"\u00c7al\u0131\u015fmayan K\u0131s\u0131m Gider Ve Zararlar\u0131(-)": {},
"\u00d6nceki D\u00f6nem Gider Ve Zararlar\u0131(-)": {}
},
"Sat\u0131\u015f \u0130ndirimleri (-)": {
"Di\u011fer \u0130ndirimler": {},
"Sat\u0131\u015f \u0130ndirimleri(-)": {},
"Sat\u0131\u015ftan \u0130adeler(-)": {}
},
"Sat\u0131\u015flar\u0131n Maliyeti(-)": {
"Di\u011fer Sat\u0131\u015flar\u0131n Maliyeti(-)": {},
"Sat\u0131lan Hizmet Maliyeti(-)": {},
"Sat\u0131lan Mamuller Maliyeti(-)": {},
"Sat\u0131lan Ticari Mallar Maliyeti(-)": {}
},
"root_type": ""
},
"K\u0131sa Vadeli Yabanc\u0131 Kaynaklar": {
"Al\u0131nan Avanslar": {
"Al\u0131nan Di\u011fer Avanslar": {
"account_type": "Payable"
},
"Al\u0131nan Sipari\u015f Avanslar\u0131": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"Bor\u00e7 ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
"Di\u011fer Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
"account_type": "Payable"
},
"D\u00f6nem K\u00e2r\u0131 Vergi Ve Di\u011fer Yasal Y\u00fck\u00fcml\u00fcl\u00fck Kar\u015f\u0131l\u0131klar\u0131": {
"account_type": "Tax"
},
"D\u00f6nem K\u00e2r\u0131n\u0131n Pe\u015fin \u00d6denen Vergi Ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler(-)": {
"account_type": "Tax"
},
"K\u0131dem Tazminat\u0131 Kar\u015f\u0131l\u0131\u011f\u0131": {},
"Maliyet Giderleri Kar\u015f\u0131l\u0131\u011f\u0131": {},
"account_type": "Payable"
},
"Di\u011fer Bor\u00e7lar": {
"Ba\u011fl\u0131 Ortakl\u0131klara Bor\u00e7lar": {
"account_type": "Payable"
},
"Di\u011fer Bor\u00e7 Senetleri Reeskontu(-)": {
"account_type": "Payable"
},
"Di\u011fer \u00c7e\u015fitli Bor\u00e7lar": {
"account_type": "Payable"
},
"Ortaklara Bor\u00e7lar": {
"account_type": "Payable"
},
"Personele Bor\u00e7lar": {
"account_type": "Payable"
},
"account_type": "Payable",
"\u0130\u015ftiraklere Bor\u00e7lar": {
"account_type": "Payable"
}
},
"Di\u011fer K\u0131sa Vadeli Yabanc\u0131 Kaynaklar": {
"Di\u011fer KDV": {
"account_type": "Tax"
},
"Di\u011fer \u00c7e\u015fitli Yabanc\u0131 Kaynaklar": {},
"Hesaplanan KDV": {
"account_type": "Tax"
},
"Merkez Ve \u015eubeler Cari Hesab\u0131": {},
"Say\u0131m Ve Tesell\u00fcm Fazlalar\u0131": {},
"account_type": "Payable"
},
"Gelecek Aylara Ait Gelirler Ve Gider Tahakkuklar\u0131": {
"Gelecek Aylara Ait Gelirler": {},
"Gider Tahakkuklar\u0131": {}
},
"Mali Bor\u00e7lar": {
"Banka Kredileri": {
"account_type": "Payable"
},
"Di\u011fer Mali Bor\u00e7lar": {
"account_type": "Payable"
},
"Ertelenmi\u015f Finansal Kiralama Bor\u00e7lanma Maliyetleri(-)": {
"account_type": "Payable"
},
"Finansal Kiralama \u0130\u015flemlerinden Bor\u00e7lar": {
"account_type": "Payable"
},
"Menkul K\u0131ymetler \u0130hra\u00e7 Fark\u0131(-)": {
"account_type": "Payable"
},
"Tahvil Anapara Bor\u00e7, Taksit Ve Faizleri": {
"account_type": "Payable"
},
"Uzun Vadeli Kredilerin Anapara Taksitleri Ve Faizleri": {
"account_type": "Payable"
},
"account_type": "Payable",
"\u00c7\u0131kar\u0131lan Bonolar Ve Senetler": {
"account_type": "Payable"
},
"\u00c7\u0131kar\u0131lm\u0131\u015f Di\u011fer Menkul K\u0131ymetler": {
"account_type": "Payable"
}
},
"Ticari Bor\u00e7lar": {
"Al\u0131nan Depozito Ve Teminatlar": {
"account_type": "Payable"
},
"Bor\u00e7 Senetleri": {
"account_type": "Payable"
},
"Bor\u00e7 Senetleri Reeskontu(-)": {
"account_type": "Payable"
},
"Di\u011fer Ticari Bor\u00e7lar": {
"account_type": "Payable"
},
"Sat\u0131c\u0131lar": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Hakedi\u015fleri": {
"350 Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Hakedi\u015fleri Bedelleri": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"root_type": "",
"\u00d6denecek Vergi ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
"Vadesi Ge\u00e7mi\u015f, Ertelenmi\u015f Veya Taksitlendirilmi\u015f Vergi Ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
"account_type": "Tax"
},
"account_type": "Tax",
"\u00d6denecek Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
"account_type": "Tax"
},
"\u00d6denecek Sosyal G\u00fcvenl\u00fck Kesintileri": {
"account_type": "Tax"
},
"\u00d6denecek Vergi Ve Fonlar": {
"account_type": "Tax"
}
}
},
"Maliyet Hesaplar\u0131": {
"Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
"Direkt \u0130lk Madde Ve Malzeme Giderleri": {
"Direk \u0130lk Madde Ve Malzeme Giderleri Hesab\u0131": {},
"Direkt \u0130lk Madde Ve Malzeme Fiyat Fark\u0131": {},
"Direkt \u0130lk Madde Ve Malzeme Miktar Fark\u0131": {},
"Direkt \u0130lk Madde Ve Malzeme Yans\u0131tma Hesab\u0131": {}
},
"Direkt \u0130\u015f\u00e7ilik Giderleri": {
"Direkt \u0130\u015f\u00e7ilik Giderleri": {},
"Direkt \u0130\u015f\u00e7ilik Giderleri Yans\u0131tma Hesab\u0131": {},
"Direkt \u0130\u015f\u00e7ilik S\u00fcre Farklar\u0131": {},
"Direkt \u0130\u015f\u00e7ilik \u00dccret Farklar\u0131": {}
},
"Finansman Giderleri": {
"Finansman Giderleri": {},
"Finansman Giderleri Fark Hesab\u0131": {},
"Finansman Giderleri Yans\u0131tma Hesab\u0131": {}
},
"Genel Y\u00f6netim Giderleri": {
"Genel Y\u00f6netim Gider Farklar\u0131 Hesab\u0131": {},
"Genel Y\u00f6netim Giderleri": {},
"Genel Y\u00f6netim Giderleri Yans\u0131tma Hesab\u0131": {}
},
"Genel \u00dcretim Giderleri": {
"Genel \u00dcretim Giderleri": {},
"Genel \u00dcretim Giderleri B\u00fct\u00e7e Farklar\u0131": {},
"Genel \u00dcretim Giderleri Kapasite Farklar\u0131": {},
"Genel \u00dcretim Giderleri Verimlilik Giderleri": {},
"Genel \u00dcretim Giderleri Yans\u0131tma Hesab\u0131": {}
},
"Hizmet \u00dcretim Maliyeti": {
"Hizmet \u00dcretim Maliyeti": {},
"Hizmet \u00dcretim Maliyeti Fark Hesaplar\u0131": {},
"Hizmet \u00dcretim Maliyeti Yans\u0131tma Hesab\u0131": {}
},
"Maliyet Muhasebesi Ba\u011flant\u0131 Hesaplar\u0131": {
"Maliyet Muhasebesi Ba\u011flant\u0131 Hesab\u0131": {},
"Maliyet Muhasebesi Yans\u0131tma Hesab\u0131": {}
},
"Pazarlama, Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri": {
"Atra\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
"Pazarlama Sat\u0131\u015f Ve Dag\u0131t\u0131m Giderleri Yans\u0131tma Hesab\u0131": {},
"Pazarlama Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri Fark Hesab\u0131": {}
},
"root_type": ""
},
"Naz\u0131m Hesaplar": {
"root_type": ""
},
"Serbest Hesaplar": {
"root_type": ""
},
"Uzun Vadeli Yabanc\u0131 Kaynaklar": {
"Al\u0131nan Avanslar": {
"Al\u0131nan Di\u011fer Avanslar": {
"account_type": "Payable"
},
"Al\u0131nan Sipari\u015f Avanslar\u0131": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
"Di\u011fer Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
"account_type": "Payable"
},
"K\u0131dem Tazminat\u0131 Kar\u015f\u0131l\u0131\u011f\u0131": {},
"account_type": "Payable"
},
"Di\u011fer Bor\u00e7lar": {
"Ba\u011fl\u0131 Ortakl\u0131klara Bor\u00e7lar": {
"account_type": "Payable"
},
"Di\u011fer Bor\u00e7 Senetleri Reeskontu(-)": {
"account_type": "Payable"
},
"Di\u011fer \u00c7e\u015fitli Bor\u00e7lar": {
"account_type": "Payable"
},
"Kamuya Olan Ertelenmi\u015f Veya Taksitlendirilmi\u015f Bor\u00e7lar": {
"account_type": "Payable"
},
"Ortaklara Bor\u00e7lar": {
"account_type": "Payable"
},
"account_type": "Payable",
"\u0130\u015ftiraklere Bor\u00e7lar": {
"account_type": "Payable"
}
},
"Di\u011fer Uzun Vadeli Yabanc\u0131 Kaynaklar": {
"Di\u011fer \u00c7e\u015fitli Uzun Vadeli Yabanc\u0131 Kaynaklar": {
"account_type": "Payable"
},
"Gelecek Y\u0131llara Ertelenmi\u015f Veya Terkin Edilecek KDV": {
"account_type": "Payable"
},
"Tesise Kat\u0131lma Paylar\u0131": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"Gelecek Y\u0131llara Ait Gelirler Ve Gider Tahakkuklar\u0131": {
"Gelecek Y\u0131llara Ait Gelirler": {},
"Gider Tahakkuklar\u0131": {}
},
"Mali Bor\u00e7lar": {
"Banka Kredileri": {
"account_type": "Payable"
},
"Di\u011fer Mali Bor\u00e7lar": {
"account_type": "Payable"
},
"Ertelenmi\u015f Finansal Kiralama Bor\u00e7lanma Maliyetleri(-)": {
"account_type": "Payable"
},
"Finansal Kiralama \u0130\u015flemlerinden Bor\u00e7lar": {
"account_type": "Payable"
},
"Menkul K\u0131ymetler \u0130hra\u00e7 Fark\u0131(-)": {
"account_type": "Payable"
},
"account_type": "Payable",
"\u00c7\u0131kar\u0131lm\u0131\u015f Di\u011fer Menkul K\u0131ymetler": {
"account_type": "Payable"
},
"\u00c7\u0131kar\u0131lm\u0131\u015f Tahviller": {
"account_type": "Payable"
}
},
"Ticari Bor\u00e7lar": {
"Al\u0131nan Depozito Ve Teminatlar": {
"account_type": "Payable"
},
"Bor\u00e7 Senetleri": {
"account_type": "Payable"
},
"Bor\u00e7 Senetleri Reeskontu(-)": {
"account_type": "Payable"
},
"Di\u011fer Ticari Bor\u00e7lar": {
"account_type": "Payable"
},
"Sat\u0131c\u0131lar": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"root_type": ""
},
"\u00d6z Kaynaklar": {
"D\u00f6nem Net K\u00e2r\u0131 (Zarar\u0131)": {
"D\u00f6nem Net K\u00e2r\u0131": {},
"D\u00f6nem Net Zarar\u0131(-)": {}
},
"Ge\u00e7mi\u015f Y\u0131llar K\u00e2rlar\u0131": {
"Ge\u00e7mi\u015f Y\u0131llar K\u00e2rlar\u0131": {}
},
"Ge\u00e7mi\u015f Y\u0131llar Zararlar\u0131(-)": {
"Ge\u00e7mi\u015f Y\u0131llar Zararlar\u0131(-)": {}
},
"K\u00e2r Yedekleri": {
"Di\u011fer K\u00e2r Yedekleri": {},
"Ola\u011fan\u00fcst\u00fc Yedekler": {},
"Stat\u00fc Yedekleri": {},
"Yasal Yedekler": {},
"\u00d6zel Fonlar": {}
},
"Sermaye Yedekleri": {
"Di\u011fer Sermaye Yedekleri": {},
"Hisse Senedi \u0130ptal K\u00e2rlar\u0131": {},
"Hisse Senetleri \u0130hra\u00e7 Primleri": {},
"Maddi Duran Varl\u0131k Yeniden De\u011ferlenme Art\u0131\u015flar\u0131": {},
"Maliyet Art\u0131\u015flar\u0131 Fonu": {},
"\u0130\u015ftirakler Yeniden De\u011ferleme Art\u0131\u015flar\u0131": {}
},
"root_type": "",
"\u00d6denmi\u015f Sermaye": {
"Sermaye": {},
"\u00d6denmi\u015f Sermaye(-)": {
"account_type": "Payable"
}
}
}
}
}

View File

@@ -56,9 +56,7 @@
"Constru\u00e7\u00f5es em Andamento de Im\u00f3veis Destinados \u00e0 Venda": {}, "Constru\u00e7\u00f5es em Andamento de Im\u00f3veis Destinados \u00e0 Venda": {},
"Estoques Destinados \u00e0 Doa\u00e7\u00e3o": {}, "Estoques Destinados \u00e0 Doa\u00e7\u00e3o": {},
"Im\u00f3veis Destinados \u00e0 Venda": {}, "Im\u00f3veis Destinados \u00e0 Venda": {},
"Insumos (materiais diretos)": { "Insumos (materiais diretos)": {},
"account_type": "Stock"
},
"Insumos Agropecu\u00e1rios": {}, "Insumos Agropecu\u00e1rios": {},
"Mercadorias para Revenda": {}, "Mercadorias para Revenda": {},
"Outras 11": {}, "Outras 11": {},
@@ -148,65 +146,6 @@
"root_type": "Asset" "root_type": "Asset"
}, },
"CUSTOS DE PRODU\u00c7\u00c3O": { "CUSTOS DE PRODU\u00c7\u00c3O": {
"CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
"CUSTO DOS PRODUTOS VENDIDOS": {
"CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
"Custos dos Produtos Vendidos em Geral": {
"account_type": "Cost of Goods Sold"
},
"Outros Custos 4": {},
"account_type": "Cost of Goods Sold"
},
"CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
"Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {},
"Custos dos Produtos para Assist\u00eancia Social - Vendidos": {},
"Outras": {}
},
"CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
"Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {},
"Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {},
"Outros Custos 6": {}
},
"CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
"Custos dos Produtos para Sa\u00fade - Gratuidades": {},
"Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {},
"Outros Custos 5": {}
},
"account_type": "Cost of Goods Sold"
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS": {
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
"Custo dos Servi\u00e7os Prestados em Geral": {},
"Outros Custos": {}
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {},
"Custo dos Servi\u00e7os Prestados a Gratuidade 1": {},
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {},
"Outros Custos 2": {}
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
"Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {},
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {},
"Custo dos Servi\u00e7os Prestados a Gratuidade": {},
"Custo dos Servi\u00e7os Prestados ao PROUNI": {},
"Outros Custos 1": {}
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {},
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {},
"Custo dos Servi\u00e7os Prestados a Gratuidade 2": {},
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {},
"Outros Custos 3": {}
}
}
},
"CUSTO DOS BENS E SERVI\u00c7OS PRODUZIDOS": { "CUSTO DOS BENS E SERVI\u00c7OS PRODUZIDOS": {
"CUSTO DOS PRODUTOS DE FABRICA\u00c7\u00c3O PR\u00d3PRIA PRODUZIDOS": { "CUSTO DOS PRODUTOS DE FABRICA\u00c7\u00c3O PR\u00d3PRIA PRODUZIDOS": {
"Alimenta\u00e7\u00e3o do Trabalhador": {}, "Alimenta\u00e7\u00e3o do Trabalhador": {},
@@ -682,9 +621,7 @@
"Receita das Unidades Imobili\u00e1rias Vendidas": {}, "Receita das Unidades Imobili\u00e1rias Vendidas": {},
"Receita de Exporta\u00e7\u00e3o Direta de Mercadorias e Produtos": {}, "Receita de Exporta\u00e7\u00e3o Direta de Mercadorias e Produtos": {},
"Receita de Exporta\u00e7\u00e3o de Servi\u00e7os": {}, "Receita de Exporta\u00e7\u00e3o de Servi\u00e7os": {},
"Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": { "Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": {},
"account_type": "Income Account"
},
"Receita de Vendas de Mercadorias e Produtos a Comercial Exportadora com Fim Espec\u00edfico de Exporta\u00e7\u00e3o": {} "Receita de Vendas de Mercadorias e Produtos a Comercial Exportadora com Fim Espec\u00edfico de Exporta\u00e7\u00e3o": {}
} }
} }
@@ -708,6 +645,65 @@
} }
}, },
"RESULTADO OPERACIONAL": { "RESULTADO OPERACIONAL": {
"CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
"CUSTO DOS PRODUTOS VENDIDOS": {
"CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
"Custos dos Produtos Vendidos em Geral": {
"account_type": "Cost of Goods Sold"
},
"Outros Custos 4": {},
"account_type": "Cost of Goods Sold"
},
"CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
"Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {},
"Custos dos Produtos para Assist\u00eancia Social - Vendidos": {},
"Outras": {}
},
"CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
"Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {},
"Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {},
"Outros Custos 6": {}
},
"CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
"Custos dos Produtos para Sa\u00fade - Gratuidades": {},
"Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {},
"Outros Custos 5": {}
},
"account_type": "Cost of Goods Sold"
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS": {
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
"Custo dos Servi\u00e7os Prestados em Geral": {},
"Outros Custos": {}
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {},
"Custo dos Servi\u00e7os Prestados a Gratuidade 1": {},
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {},
"Outros Custos 2": {}
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
"Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {},
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {},
"Custo dos Servi\u00e7os Prestados a Gratuidade": {},
"Custo dos Servi\u00e7os Prestados ao PROUNI": {},
"Outros Custos 1": {}
},
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {},
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {},
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {},
"Custo dos Servi\u00e7os Prestados a Gratuidade 2": {},
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {},
"Outros Custos 3": {}
}
}
},
"DESPESAS OPERACIONAIS": { "DESPESAS OPERACIONAIS": {
"DESPESAS OPERACIONAIS 1": { "DESPESAS OPERACIONAIS 1": {
"DESPESAS OPERACIONAIS 2": { "DESPESAS OPERACIONAIS 2": {

View File

@@ -33,9 +33,7 @@
}, },
"Stocks": { "Stocks": {
"Mati\u00e8res premi\u00e8res": {}, "Mati\u00e8res premi\u00e8res": {},
"Stock de produits fini": { "Stock de produits fini": {},
"account_type": "Stock"
},
"Stock exp\u00e9di\u00e9 non-factur\u00e9": {}, "Stock exp\u00e9di\u00e9 non-factur\u00e9": {},
"Travaux en cours": {}, "Travaux en cours": {},
"account_type": "Stock" "account_type": "Stock"
@@ -397,11 +395,9 @@
}, },
"Produits": { "Produits": {
"Revenus de ventes": { "Revenus de ventes": {
"Escomptes de volume sur ventes": {}, " Escomptes de volume sur ventes": {},
"Autres produits d'exploitation": {}, "Autres produits d'exploitation": {},
"Ventes": { "Ventes": {},
"account_type": "Income Account"
},
"Ventes avec des provinces harmonis\u00e9es": {}, "Ventes avec des provinces harmonis\u00e9es": {},
"Ventes avec des provinces non-harmonis\u00e9es": {}, "Ventes avec des provinces non-harmonis\u00e9es": {},
"Ventes \u00e0 l'\u00e9tranger": {} "Ventes \u00e0 l'\u00e9tranger": {}

View File

@@ -53,13 +53,8 @@
}, },
"II. Forderungen und sonstige Vermögensgegenstände": { "II. Forderungen und sonstige Vermögensgegenstände": {
"is_group": 1, "is_group": 1,
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": { "Ford. a. Lieferungen und Leistungen": {
"account_number": "1400", "account_number": "1400",
"account_type": "Receivable",
"is_group": 1
},
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1410",
"account_type": "Receivable" "account_type": "Receivable"
}, },
"Durchlaufende Posten": { "Durchlaufende Posten": {
@@ -185,13 +180,8 @@
}, },
"IV. Verbindlichkeiten aus Lieferungen und Leistungen": { "IV. Verbindlichkeiten aus Lieferungen und Leistungen": {
"is_group": 1, "is_group": 1,
"Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": { "Verbindlichkeiten aus Lieferungen u. Leistungen": {
"account_number": "1600", "account_number": "1600",
"account_type": "Payable",
"is_group": 1
},
"Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1610",
"account_type": "Payable" "account_type": "Payable"
} }
}, },

View File

@@ -1525,8 +1525,7 @@
"41-Clients et comptes rattach\u00e9s (PASSIF)": { "41-Clients et comptes rattach\u00e9s (PASSIF)": {
"Clients cr\u00e9diteurs": { "Clients cr\u00e9diteurs": {
"Clients - Avances et acomptes re\u00e7us sur commandes": { "Clients - Avances et acomptes re\u00e7us sur commandes": {
"account_number": "4191", "account_number": "4191"
"account_type": "Income Account"
}, },
"Clients - Dettes pour emballages et mat\u00e9riels consign\u00e9s": { "Clients - Dettes pour emballages et mat\u00e9riels consign\u00e9s": {
"account_number": "4196" "account_number": "4196"
@@ -3142,4 +3141,4 @@
"account_number": "7" "account_number": "7"
} }
} }
} }

View File

@@ -1,6 +1,4 @@
{ {
"country_code": "hu",
"name": "Hungary - Chart of Accounts for Microenterprises",
"tree": { "tree": {
"SZ\u00c1MLAOSZT\u00c1LY BEFEKTETETT ESZK\u00d6Z\u00d6K": { "SZ\u00c1MLAOSZT\u00c1LY BEFEKTETETT ESZK\u00d6Z\u00d6K": {
"account_number": 1, "account_number": 1,
@@ -1653,4 +1651,4 @@
} }
} }
} }
} }

View File

@@ -36,16 +36,16 @@
} }
}, },
"Fixed Assets": { "Fixed Assets": {
"Capital Equipment": { "Capital Equipments": {
"account_type": "Fixed Asset" "account_type": "Fixed Asset"
}, },
"Electronic Equipment": { "Electronic Equipments": {
"account_type": "Fixed Asset" "account_type": "Fixed Asset"
}, },
"Furniture and Fixtures": { "Furnitures and Fixtures": {
"account_type": "Fixed Asset" "account_type": "Fixed Asset"
}, },
"Office Equipment": { "Office Equipments": {
"account_type": "Fixed Asset" "account_type": "Fixed Asset"
}, },
"Plants and Machineries": { "Plants and Machineries": {
@@ -109,8 +109,7 @@
"Utility Expenses": {}, "Utility Expenses": {},
"Write Off": {}, "Write Off": {},
"Exchange Gain/Loss": {}, "Exchange Gain/Loss": {},
"Gain/Loss on Asset Disposal": {}, "Gain/Loss on Asset Disposal": {}
"Impairment": {}
}, },
"root_type": "Expense" "root_type": "Expense"
}, },
@@ -133,8 +132,7 @@
"Source of Funds (Liabilities)": { "Source of Funds (Liabilities)": {
"Capital Account": { "Capital Account": {
"Reserves and Surplus": {}, "Reserves and Surplus": {},
"Shareholders Funds": {}, "Shareholders Funds": {}
"Revaluation Surplus": {}
}, },
"Current Liabilities": { "Current Liabilities": {
"Accounts Payable": { "Accounts Payable": {

View File

@@ -1,6 +1,6 @@
{ {
"country_code": "ni", "country_code": "ni",
"name": "Nicaragua - Catálogo de Cuentas", "name": "Nicaragua - Catalogo de Cuentas",
"tree": { "tree": {
"Activo": { "Activo": {
"Activo Corriente": { "Activo Corriente": {
@@ -491,4 +491,4 @@
"root_type": "Liability" "root_type": "Liability"
} }
} }
} }

View File

@@ -23,13 +23,13 @@ def get():
_("Tax Assets"): {"is_group": 1}, _("Tax Assets"): {"is_group": 1},
}, },
_("Fixed Assets"): { _("Fixed Assets"): {
_("Capital Equipment"): {"account_type": "Fixed Asset"}, _("Capital Equipments"): {"account_type": "Fixed Asset"},
_("Electronic Equipment"): {"account_type": "Fixed Asset"}, _("Electronic Equipments"): {"account_type": "Fixed Asset"},
_("Furniture and Fixtures"): {"account_type": "Fixed Asset"}, _("Furnitures and Fixtures"): {"account_type": "Fixed Asset"},
_("Office Equipment"): {"account_type": "Fixed Asset"}, _("Office Equipments"): {"account_type": "Fixed Asset"},
_("Plants and Machineries"): {"account_type": "Fixed Asset"}, _("Plants and Machineries"): {"account_type": "Fixed Asset"},
_("Buildings"): {"account_type": "Fixed Asset"}, _("Buildings"): {"account_type": "Fixed Asset"},
_("Software"): {"account_type": "Fixed Asset"}, _("Softwares"): {"account_type": "Fixed Asset"},
_("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"}, _("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
_("CWIP Account"): { _("CWIP Account"): {
"account_type": "Capital Work in Progress", "account_type": "Capital Work in Progress",
@@ -72,7 +72,6 @@ def get():
_("Write Off"): {}, _("Write Off"): {},
_("Exchange Gain/Loss"): {}, _("Exchange Gain/Loss"): {},
_("Gain/Loss on Asset Disposal"): {}, _("Gain/Loss on Asset Disposal"): {},
_("Impairment"): {},
}, },
"root_type": "Expense", "root_type": "Expense",
}, },
@@ -105,7 +104,6 @@ def get():
_("Dividends Paid"): {"account_type": "Equity"}, _("Dividends Paid"): {"account_type": "Equity"},
_("Opening Balance Equity"): {"account_type": "Equity"}, _("Opening Balance Equity"): {"account_type": "Equity"},
_("Retained Earnings"): {"account_type": "Equity"}, _("Retained Earnings"): {"account_type": "Equity"},
_("Revaluation Surplus"): {"account_type": "Equity"},
"root_type": "Equity", "root_type": "Equity",
}, },
} }

View File

@@ -36,13 +36,13 @@ def get():
"account_number": "1100-1600", "account_number": "1100-1600",
}, },
_("Fixed Assets"): { _("Fixed Assets"): {
_("Capital Equipment"): {"account_type": "Fixed Asset", "account_number": "1710"}, _("Capital Equipments"): {"account_type": "Fixed Asset", "account_number": "1710"},
_("Electronic Equipment"): {"account_type": "Fixed Asset", "account_number": "1720"}, _("Electronic Equipments"): {"account_type": "Fixed Asset", "account_number": "1720"},
_("Furniture and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"}, _("Furnitures and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
_("Office Equipment"): {"account_type": "Fixed Asset", "account_number": "1740"}, _("Office Equipments"): {"account_type": "Fixed Asset", "account_number": "1740"},
_("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"}, _("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
_("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"}, _("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
_("Software"): {"account_type": "Fixed Asset", "account_number": "1770"}, _("Softwares"): {"account_type": "Fixed Asset", "account_number": "1770"},
_("Accumulated Depreciation"): { _("Accumulated Depreciation"): {
"account_type": "Accumulated Depreciation", "account_type": "Accumulated Depreciation",
"account_number": "1780", "account_number": "1780",

View File

@@ -1,10 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import unittest import unittest
import frappe import frappe
from frappe.tests import IntegrationTestCase from frappe.test_runner import make_test_records
from frappe.utils import nowdate
from erpnext.accounts.doctype.account.account import ( from erpnext.accounts.doctype.account.account import (
InvalidAccountMergeError, InvalidAccountMergeError,
@@ -13,10 +14,10 @@ from erpnext.accounts.doctype.account.account import (
) )
from erpnext.stock import get_company_default_inventory_account, get_warehouse_account from erpnext.stock import get_company_default_inventory_account, get_warehouse_account
EXTRA_TEST_RECORD_DEPENDENCIES = ["Company"] test_dependencies = ["Company"]
class TestAccount(IntegrationTestCase): class TestAccount(unittest.TestCase):
def test_rename_account(self): def test_rename_account(self):
if not frappe.db.exists("Account", "1210 - Debtors - _TC"): if not frappe.db.exists("Account", "1210 - Debtors - _TC"):
acc = frappe.new_doc("Account") acc = frappe.new_doc("Account")
@@ -118,7 +119,7 @@ class TestAccount(IntegrationTestCase):
InvalidAccountMergeError, InvalidAccountMergeError,
merge_account, merge_account,
"Capital Stock - _TC", "Capital Stock - _TC",
"Software - _TC", "Softwares - _TC",
) )
# Raise error as currency doesn't match # Raise error as currency doesn't match
@@ -201,6 +202,8 @@ class TestAccount(IntegrationTestCase):
In a parent->child company setup, child should inherit parent account currency if explicitly specified. In a parent->child company setup, child should inherit parent account currency if explicitly specified.
""" """
make_test_records("Company")
frappe.local.flags.pop("ignore_root_company_validation", None) frappe.local.flags.pop("ignore_root_company_validation", None)
def create_bank_account(): def create_bank_account():
@@ -257,20 +260,28 @@ class TestAccount(IntegrationTestCase):
acc.insert() acc.insert()
self.assertTrue( self.assertTrue(
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}) frappe.db.exists(
"Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}
)
) )
self.assertTrue( self.assertTrue(
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}) frappe.db.exists(
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
)
) )
# Try renaming child company account # Try renaming child company account
acc_tc_5 = frappe.db.get_value( acc_tc_5 = frappe.db.get_value(
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"} "Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
) )
self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account") self.assertRaises(
frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account"
)
# Rename child company account with allow_account_creation_against_child_company enabled # Rename child company account with allow_account_creation_against_child_company enabled
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1) frappe.db.set_value(
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 1
)
update_account_number(acc_tc_5, "Test Modified Account") update_account_number(acc_tc_5, "Test Modified Account")
self.assertTrue( self.assertTrue(
@@ -279,7 +290,9 @@ class TestAccount(IntegrationTestCase):
) )
) )
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0) frappe.db.set_value(
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
)
to_delete = [ to_delete = [
"Test Group Account - _TC3", "Test Group Account - _TC3",
@@ -304,27 +317,16 @@ class TestAccount(IntegrationTestCase):
self.assertEqual(acc.account_currency, "INR") self.assertEqual(acc.account_currency, "INR")
# Make a JV against this account # Make a JV against this account
make_journal_entry("Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True) make_journal_entry(
"Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True
)
acc.account_currency = "USD" acc.account_currency = "USD"
self.assertRaises(frappe.ValidationError, acc.save) self.assertRaises(frappe.ValidationError, acc.save)
def test_account_balance(self):
from erpnext.accounts.utils import get_balance_on
if not frappe.db.exists("Account", "Test Percent Account %5 - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Test Percent Account %5"
acc.parent_account = "Tax Assets - _TC"
acc.company = "_Test Company"
acc.insert()
balance = get_balance_on(account="Test Percent Account %5 - _TC", date=nowdate())
self.assertEqual(balance, 0)
def _make_test_records(verbose=None): def _make_test_records(verbose=None):
from frappe.tests.utils import make_test_objects from frappe.test_runner import make_test_objects
accounts = [ accounts = [
# [account_name, parent_account, is_group] # [account_name, parent_account, is_group]

View File

@@ -0,0 +1,6 @@
[
{
"doctype": "Account",
"name": "_Test Account 1"
}
]

View File

@@ -1,3 +0,0 @@
[[Account]]
name = "_Test Account 1"

View File

@@ -129,7 +129,7 @@
"icon": "fa fa-list", "icon": "fa fa-list",
"in_create": 1, "in_create": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:05:56.710541", "modified": "2023-03-06 08:56:36.393237",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account Closing Balance", "name": "Account Closing Balance",
@@ -158,7 +158,7 @@
"role": "Auditor" "role": "Auditor"
} }
], ],
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [] "states": []
} }

View File

@@ -11,41 +11,22 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
class AccountClosingBalance(Document): class AccountClosingBalance(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
account: DF.Link | None
account_currency: DF.Link | None
closing_date: DF.Date | None
company: DF.Link | None
cost_center: DF.Link | None
credit: DF.Currency
credit_in_account_currency: DF.Currency
debit: DF.Currency
debit_in_account_currency: DF.Currency
finance_book: DF.Link | None
is_period_closing_voucher_entry: DF.Check
period_closing_voucher: DF.Link | None
project: DF.Link | None
# end: auto-generated types
pass pass
def make_closing_entries(closing_entries, voucher_name, company, closing_date): def make_closing_entries(closing_entries, voucher_name, company, closing_date):
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions()
previous_closing_entries = get_previous_closing_entries(company, closing_date, accounting_dimensions) previous_closing_entries = get_previous_closing_entries(
company, closing_date, accounting_dimensions
)
combined_entries = closing_entries + previous_closing_entries combined_entries = closing_entries + previous_closing_entries
merged_entries = aggregate_with_last_account_closing_balance(combined_entries, accounting_dimensions) merged_entries = aggregate_with_last_account_closing_balance(
combined_entries, accounting_dimensions
)
for _key, value in merged_entries.items(): for key, value in merged_entries.items():
cle = frappe.new_doc("Account Closing Balance") cle = frappe.new_doc("Account Closing Balance")
cle.update(value) cle.update(value)
cle.update(value["dimensions"]) cle.update(value["dimensions"])
@@ -113,9 +94,9 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions):
entries = [] entries = []
last_period_closing_voucher = frappe.db.get_all( last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher", "Period Closing Voucher",
filters={"docstatus": 1, "company": company, "period_end_date": ("<", closing_date)}, filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)},
fields=["name"], fields=["name"],
order_by="period_end_date desc", order_by="posting_date desc",
limit=1, limit=1,
) )

View File

@@ -2,17 +2,8 @@
# See license.txt # See license.txt
# import frappe # import frappe
from frappe.tests import IntegrationTestCase, UnitTestCase from frappe.tests.utils import FrappeTestCase
class UnitTestAccountClosingBalance(UnitTestCase): class TestAccountClosingBalance(FrappeTestCase):
"""
Unit tests for AccountClosingBalance.
Use this class for testing individual functions and methods.
"""
pass
class TestAccountClosingBalance(IntegrationTestCase):
pass pass

View File

@@ -1,89 +1,74 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on("Accounting Dimension", { frappe.ui.form.on('Accounting Dimension', {
refresh: function (frm) { refresh: function(frm) {
frm.set_query("document_type", () => { frm.set_query('document_type', () => {
let invalid_doctypes = frappe.model.core_doctypes_list; let invalid_doctypes = frappe.model.core_doctypes_list;
invalid_doctypes.push( invalid_doctypes.push('Accounting Dimension', 'Project',
"Accounting Dimension", 'Cost Center', 'Accounting Dimension Detail', 'Company');
"Project",
"Cost Center",
"Accounting Dimension Detail",
"Company"
);
return { return {
filters: { filters: {
name: ["not in", invalid_doctypes], name: ['not in', invalid_doctypes]
}, }
}; };
}); });
frm.set_query("offsetting_account", "dimension_defaults", function (doc, cdt, cdn) { frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) {
let d = locals[cdt][cdn]; let d = locals[cdt][cdn];
return { return {
filters: { filters: {
company: d.company, company: d.company,
root_type: ["in", ["Asset", "Liability"]], root_type: ["in", ["Asset", "Liability"]],
is_group: 0, is_group: 0
}, }
}; }
}); });
if (!frm.is_new()) { if (!frm.is_new()) {
frm.add_custom_button(__("Show {0}", [frm.doc.document_type]), function () { frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () {
frappe.set_route("List", frm.doc.document_type); frappe.set_route("List", frm.doc.document_type);
}); });
let button = frm.doc.disabled ? "Enable" : "Disable"; let button = frm.doc.disabled ? "Enable" : "Disable";
frm.add_custom_button(__(button), function () { frm.add_custom_button(__(button), function() {
frm.set_value("disabled", 1 - frm.doc.disabled);
frm.set_value('disabled', 1 - frm.doc.disabled);
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
args: { args: {
doc: frm.doc, doc: frm.doc
}, },
freeze: true, freeze: true,
callback: function (r) { callback: function(r) {
let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled"; let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled";
frm.save(); frm.save();
frappe.show_alert({ message: __(message), indicator: "green" }); frappe.show_alert({message:__(message), indicator:'green'});
}, }
}); });
}); });
} }
}, },
label: function (frm) { document_type: function(frm) {
frm.set_value("fieldname", frm.doc.label.replace(/ /g, "_").replace(/-/g, "_").toLowerCase());
},
document_type: function (frm) { frm.set_value('label', frm.doc.document_type);
frm.set_value("label", frm.doc.document_type); frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type));
frappe.db.get_value( frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => {
"Accounting Dimension", if (r && r.document_type) {
{ document_type: frm.doc.document_type }, frm.set_df_property('document_type', 'description', "Document type is already set as dimension");
"document_type",
(r) => {
if (r && r.document_type) {
frm.set_df_property(
"document_type",
"description",
"Document type is already set as dimension"
);
}
} }
); });
}, },
}); });
frappe.ui.form.on("Accounting Dimension Detail", { frappe.ui.form.on('Accounting Dimension Detail', {
dimension_defaults_add: function (frm, cdt, cdn) { dimension_defaults_add: function(frm, cdt, cdn) {
let row = locals[cdt][cdn]; let row = locals[cdt][cdn];
row.reference_document = frm.doc.document_type; row.reference_document = frm.doc.document_type;
}, }
}); });

View File

@@ -49,7 +49,7 @@
} }
], ],
"links": [], "links": [],
"modified": "2024-03-27 13:05:56.890002", "modified": "2021-02-08 16:37:53.936656",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting Dimension", "name": "Accounting Dimension",
@@ -80,8 +80,7 @@
"write": 1 "write": 1
} }
], ],
"sort_field": "creation", "sort_field": "modified",
"sort_order": "ASC", "sort_order": "ASC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -7,42 +7,17 @@ import json
import frappe import frappe
from frappe import _, scrub from frappe import _, scrub
from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.database.schema import validate_column_name
from frappe.model import core_doctypes_list from frappe.model import core_doctypes_list
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import cstr from frappe.utils import cstr
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
get_allowed_types_from_settings,
)
class AccountingDimension(Document): class AccountingDimension(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.accounting_dimension_detail.accounting_dimension_detail import (
AccountingDimensionDetail,
)
dimension_defaults: DF.Table[AccountingDimensionDetail]
disabled: DF.Check
document_type: DF.Link
fieldname: DF.Data | None
label: DF.Data | None
# end: auto-generated types
def before_insert(self): def before_insert(self):
self.set_fieldname_and_label() self.set_fieldname_and_label()
def validate(self): def validate(self):
if self.document_type in ( if self.document_type in core_doctypes_list + (
*core_doctypes_list,
"Accounting Dimension", "Accounting Dimension",
"Project", "Project",
"Cost Center", "Cost Center",
@@ -50,10 +25,13 @@ class AccountingDimension(Document):
"Company", "Company",
"Account", "Account",
): ):
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type) msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg) frappe.throw(msg)
exists = frappe.db.get_value("Accounting Dimension", {"document_type": self.document_type}, ["name"]) exists = frappe.db.get_value(
"Accounting Dimension", {"document_type": self.document_type}, ["name"]
)
if exists and self.is_new(): if exists and self.is_new():
frappe.throw(_("Document Type already used as a dimension")) frappe.throw(_("Document Type already used as a dimension"))
@@ -61,7 +39,6 @@ class AccountingDimension(Document):
if not self.is_new(): if not self.is_new():
self.validate_document_type_change() self.validate_document_type_change()
validate_column_name(self.fieldname)
self.validate_dimension_defaults() self.validate_dimension_defaults()
def validate_document_type_change(self): def validate_document_type_change(self):
@@ -110,9 +87,9 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
doc_count = len(get_accounting_dimensions()) doc_count = len(get_accounting_dimensions())
count = 0 count = 0
repostable_doctypes = get_allowed_types_from_settings()
for doctype in doclist: for doctype in doclist:
if (doc_count + 1) % 2 == 0: if (doc_count + 1) % 2 == 0:
insert_after_field = "dimension_col_break" insert_after_field = "dimension_col_break"
else: else:
@@ -125,7 +102,6 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
"options": doc.document_type, "options": doc.document_type,
"insert_after": insert_after_field, "insert_after": insert_after_field,
"owner": "Administrator", "owner": "Administrator",
"allow_on_submit": 1 if doctype in repostable_doctypes else 0,
} }
meta = frappe.get_meta(doctype, cached=False) meta = frappe.get_meta(doctype, cached=False)
@@ -147,7 +123,7 @@ def add_dimension_to_budget_doctype(df, doc):
df.update( df.update(
{ {
"insert_after": "cost_center", "insert_after": "cost_center",
"depends_on": f"eval:doc.budget_against == '{doc.document_type}'", "depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type),
} }
) )
@@ -181,17 +157,19 @@ def delete_accounting_dimension(doc):
frappe.db.sql( frappe.db.sql(
""" """
DELETE FROM `tabCustom Field` DELETE FROM `tabCustom Field`
WHERE fieldname = {} WHERE fieldname = %s
AND dt IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec AND dt IN (%s)"""
tuple([doc.fieldname, *doclist]), % ("%s", ", ".join(["%s"] * len(doclist))), # nosec
tuple([doc.fieldname] + doclist),
) )
frappe.db.sql( frappe.db.sql(
""" """
DELETE FROM `tabProperty Setter` DELETE FROM `tabProperty Setter`
WHERE field_name = {} WHERE field_name = %s
AND doc_type IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec AND doc_type IN (%s)"""
tuple([doc.fieldname, *doclist]), % ("%s", ", ".join(["%s"] * len(doclist))), # nosec
tuple([doc.fieldname] + doclist),
) )
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options") budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
@@ -240,6 +218,7 @@ def get_doctypes_with_dimensions():
def get_accounting_dimensions(as_list=True, filters=None): def get_accounting_dimensions(as_list=True, filters=None):
if not filters: if not filters:
filters = {"disabled": 0} filters = {"disabled": 0}
@@ -257,19 +236,18 @@ def get_accounting_dimensions(as_list=True, filters=None):
def get_checks_for_pl_and_bs_accounts(): def get_checks_for_pl_and_bs_accounts():
if frappe.flags.accounting_dimensions_details is None: dimensions = frappe.db.sql(
# nosemgrep """SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
frappe.flags.accounting_dimensions_details = frappe.db.sql( FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs WHERE p.name = c.parent""",
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c as_dict=1,
WHERE p.name = c.parent""", )
as_dict=1,
)
return frappe.flags.accounting_dimensions_details return dimensions
def get_dimension_with_children(doctype, dimensions): def get_dimension_with_children(doctype, dimensions):
if isinstance(dimensions, str): if isinstance(dimensions, str):
dimensions = [dimensions] dimensions = [dimensions]
@@ -277,7 +255,9 @@ def get_dimension_with_children(doctype, dimensions):
for dimension in dimensions: for dimension in dimensions:
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"]) lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft") children = frappe.get_all(
doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft"
)
all_dimensions += [c.name for c in children] all_dimensions += [c.name for c in children]
return all_dimensions return all_dimensions
@@ -285,10 +265,14 @@ def get_dimension_with_children(doctype, dimensions):
@frappe.whitelist() @frappe.whitelist()
def get_dimensions(with_cost_center_and_project=False): def get_dimensions(with_cost_center_and_project=False):
c = frappe.qb.DocType("Accounting Dimension Detail") c = frappe.qb.DocType("Accounting Dimension Detail")
p = frappe.qb.DocType("Accounting Dimension") p = frappe.qb.DocType("Accounting Dimension")
dimension_filters = ( dimension_filters = (
frappe.qb.from_(p).select(p.label, p.fieldname, p.document_type).where(p.disabled == 0).run(as_dict=1) frappe.qb.from_(p)
.select(p.label, p.fieldname, p.document_type)
.where(p.disabled == 0)
.run(as_dict=1)
) )
default_dimensions = ( default_dimensions = (
frappe.qb.from_(c) frappe.qb.from_(c)

View File

@@ -1,17 +1,17 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest import unittest
import frappe import frappe
from frappe.tests import IntegrationTestCase
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
EXTRA_TEST_RECORD_DEPENDENCIES = ["Cost Center", "Location", "Warehouse", "Department"] test_dependencies = ["Cost Center", "Location", "Warehouse", "Department"]
class TestAccountingDimension(IntegrationTestCase): class TestAccountingDimension(unittest.TestCase):
def setUp(self): def setUp(self):
create_dimension() create_dimension()
@@ -78,8 +78,6 @@ class TestAccountingDimension(IntegrationTestCase):
def tearDown(self): def tearDown(self):
disable_dimension() disable_dimension()
frappe.flags.accounting_dimensions_details = None
frappe.flags.dimension_filter_map = None
def create_dimension(): def create_dimension():

View File

@@ -1,5 +1,4 @@
{ {
"actions": [],
"creation": "2019-07-16 17:53:18.718831", "creation": "2019-07-16 17:53:18.718831",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -74,15 +73,13 @@
} }
], ],
"istable": 1, "istable": 1,
"links": [], "modified": "2019-08-15 11:59:09.389891",
"modified": "2024-03-27 13:05:57.056874",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting Dimension Detail", "name": "Accounting Dimension Detail",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -7,24 +7,4 @@ from frappe.model.document import Document
class AccountingDimensionDetail(Document): class AccountingDimensionDetail(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
automatically_post_balancing_accounting_entry: DF.Check
company: DF.Link | None
default_dimension: DF.DynamicLink | None
mandatory_for_bs: DF.Check
mandatory_for_pl: DF.Check
offsetting_account: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
reference_document: DF.Link | None
# end: auto-generated types
pass pass

View File

@@ -1,9 +1,10 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on("Accounting Dimension Filter", { frappe.ui.form.on('Accounting Dimension Filter', {
refresh: function (frm, cdt, cdn) { refresh: function(frm, cdt, cdn) {
let help_content = `<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);"> let help_content =
`<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
<tr><td> <tr><td>
<p> <p>
<i class="fa fa-hand-right"></i> <i class="fa fa-hand-right"></i>
@@ -12,80 +13,77 @@ frappe.ui.form.on("Accounting Dimension Filter", {
</td></tr> </td></tr>
</table>`; </table>`;
frm.set_df_property("dimension_filter_help", "options", help_content); frm.set_df_property('dimension_filter_help', 'options', help_content);
}, },
onload: function (frm) { onload: function(frm) {
frm.set_query("applicable_on_account", "accounts", function () { frm.set_query('applicable_on_account', 'accounts', function() {
return { return {
filters: { filters: {
company: frm.doc.company, 'company': frm.doc.company
}, }
}; };
}); });
frappe.db.get_list("Accounting Dimension", { fields: ["document_type"] }).then((res) => { frappe.db.get_list('Accounting Dimension',
let options = ["Cost Center", "Project"]; {fields: ['document_type']}).then((res) => {
let options = ['Cost Center', 'Project'];
res.forEach((dimension) => { res.forEach((dimension) => {
options.push(dimension.document_type); options.push(dimension.document_type);
}); });
frm.set_df_property("accounting_dimension", "options", options); frm.set_df_property('accounting_dimension', 'options', options);
}); });
frm.trigger("setup_filters"); frm.trigger('setup_filters');
}, },
setup_filters: function (frm) { setup_filters: function(frm) {
let filters = {}; let filters = {};
if (frm.doc.accounting_dimension) { if (frm.doc.accounting_dimension) {
frappe.model.with_doctype(frm.doc.accounting_dimension, function () { frappe.model.with_doctype(frm.doc.accounting_dimension, function() {
if (frappe.model.is_tree(frm.doc.accounting_dimension)) { if (frappe.model.is_tree(frm.doc.accounting_dimension)) {
filters["is_group"] = 0; filters['is_group'] = 0;
} }
if (frappe.meta.has_field(frm.doc.accounting_dimension, "company")) { if (frappe.meta.has_field(frm.doc.accounting_dimension, 'company')) {
filters["company"] = frm.doc.company; filters['company'] = frm.doc.company;
} }
frm.set_query("dimension_value", "dimensions", function () { frm.set_query('dimension_value', 'dimensions', function() {
return { return {
filters: filters, filters: filters
}; };
}); });
}); });
} }
}, },
accounting_dimension: function (frm) { accounting_dimension: function(frm) {
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( frm.fields_dict["dimensions"].grid.update_docfield_property("dimension_value", "label", frm.doc.accounting_dimension);
"dimension_value",
"label",
frm.doc.accounting_dimension
);
frm.refresh_field("dimensions"); frm.refresh_field("dimensions");
frm.trigger("setup_filters"); frm.trigger('setup_filters');
}, },
apply_restriction_on_values: function (frm) { apply_restriction_on_values: function(frm) {
/** If restriction on values is not applied, we should set "allow_or_restrict" to "Restrict" with an empty allowed dimension table. /** If restriction on values is not applied, we should set "allow_or_restrict" to "Restrict" with an empty allowed dimension table.
* Hence it's not "restricted" on any value. * Hence it's not "restricted" on any value.
*/ */
if (!frm.doc.apply_restriction_on_values) { if (!frm.doc.apply_restriction_on_values) {
frm.set_value("allow_or_restrict", "Restrict"); frm.set_value("allow_or_restrict", "Restrict");
frm.clear_table("dimensions"); frm.clear_table("dimensions");
frm.refresh_field("dimensions"); frm.refresh_field("dimensions");
} }
}, }
}); });
frappe.ui.form.on("Allowed Dimension", { frappe.ui.form.on('Allowed Dimension', {
dimensions_add: function (frm, cdt, cdn) { dimensions_add: function(frm, cdt, cdn) {
let row = locals[cdt][cdn]; let row = locals[cdt][cdn];
row.accounting_dimension = frm.doc.accounting_dimension; row.accounting_dimension = frm.doc.accounting_dimension;
frm.refresh_field("dimensions"); frm.refresh_field("dimensions");
}, }
}); });

View File

@@ -94,7 +94,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:05:57.199186", "modified": "2023-06-07 14:59:41.869117",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting Dimension Filter", "name": "Accounting Dimension Filter",
@@ -139,7 +139,7 @@
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1

View File

@@ -8,28 +8,6 @@ from frappe.model.document import Document
class AccountingDimensionFilter(Document): class AccountingDimensionFilter(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.allowed_dimension.allowed_dimension import AllowedDimension
from erpnext.accounts.doctype.applicable_on_account.applicable_on_account import (
ApplicableOnAccount,
)
accounting_dimension: DF.Literal
accounts: DF.Table[ApplicableOnAccount]
allow_or_restrict: DF.Literal["Allow", "Restrict"]
apply_restriction_on_values: DF.Check
company: DF.Link
dimensions: DF.Table[AllowedDimension]
disabled: DF.Check
# end: auto-generated types
def before_save(self): def before_save(self):
# If restriction is not applied on values, then remove all the dimensions and set allow_or_restrict to Restrict # If restriction is not applied on values, then remove all the dimensions and set allow_or_restrict to Restrict
if not self.apply_restriction_on_values: if not self.apply_restriction_on_values:
@@ -66,39 +44,37 @@ class AccountingDimensionFilter(Document):
def get_dimension_filter_map(): def get_dimension_filter_map():
if not frappe.flags.get("dimension_filter_map"): filters = frappe.db.sql(
filters = frappe.db.sql( """
""" SELECT
SELECT a.applicable_on_account, d.dimension_value, p.accounting_dimension,
a.applicable_on_account, d.dimension_value, p.accounting_dimension, p.allow_or_restrict, a.is_mandatory
p.allow_or_restrict, a.is_mandatory FROM
FROM `tabApplicable On Account` a,
`tabApplicable On Account` a, `tabAccounting Dimension Filter` p
`tabAccounting Dimension Filter` p LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name WHERE
WHERE p.name = a.parent
p.name = a.parent AND p.disabled = 0
AND p.disabled = 0 """,
""", as_dict=1,
as_dict=1, )
dimension_filter_map = {}
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
) )
dimension_filter_map = {} return dimension_filter_map
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
)
frappe.flags.dimension_filter_map = dimension_filter_map
return frappe.flags.dimension_filter_map
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory): def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):

View File

@@ -12,7 +12,7 @@ from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension imp
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.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
EXTRA_TEST_RECORD_DEPENDENCIES = ["Location", "Cost Center", "Department"] test_dependencies = ["Location", "Cost Center", "Department"]
class TestAccountingDimensionFilter(unittest.TestCase): class TestAccountingDimensionFilter(unittest.TestCase):
@@ -47,8 +47,6 @@ class TestAccountingDimensionFilter(unittest.TestCase):
def tearDown(self): def tearDown(self):
disable_dimension_filter() disable_dimension_filter()
disable_dimension() disable_dimension()
frappe.flags.accounting_dimensions_details = None
frappe.flags.dimension_filter_map = None
for si in self.invoice_list: for si in self.invoice_list:
si.load_from_db() si.load_from_db()
@@ -57,7 +55,9 @@ class TestAccountingDimensionFilter(unittest.TestCase):
def create_accounting_dimension_filter(): def create_accounting_dimension_filter():
if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}): if not frappe.db.get_value(
"Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
):
frappe.get_doc( frappe.get_doc(
{ {
"doctype": "Accounting Dimension Filter", "doctype": "Accounting Dimension Filter",

View File

@@ -1,33 +1,30 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on("Accounting Period", { frappe.ui.form.on('Accounting Period', {
onload: function (frm) { onload: function(frm) {
if ( if(frm.doc.closed_documents.length === 0 || (frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)) {
frm.doc.closed_documents.length === 0 ||
(frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)
) {
frappe.call({ frappe.call({
method: "get_doctypes_for_closing", method: "get_doctypes_for_closing",
doc: frm.doc, doc:frm.doc,
callback: function (r) { callback: function(r) {
if (r.message) { if(r.message) {
cur_frm.clear_table("closed_documents"); cur_frm.clear_table("closed_documents");
r.message.forEach(function (element) { r.message.forEach(function(element) {
var c = frm.add_child("closed_documents"); var c = frm.add_child("closed_documents");
c.document_type = element.document_type; c.document_type = element.document_type;
c.closed = element.closed; c.closed = element.closed;
}); });
refresh_field("closed_documents"); refresh_field("closed_documents");
} }
}, }
}); });
} }
frm.set_query("document_type", "closed_documents", () => { frm.set_query("document_type", "closed_documents", () => {
return { return {
query: "erpnext.controllers.queries.get_doctypes_for_closing", query: "erpnext.controllers.queries.get_doctypes_for_closing",
}; }
}); });
}, }
}); });

View File

@@ -1,112 +1,317 @@
{ {
"actions": [], "allow_copy": 0,
"autoname": "field:period_name", "allow_guest_to_view": 0,
"creation": "2018-04-13 18:50:14.672323", "allow_import": 0,
"doctype": "DocType", "allow_rename": 0,
"editable_grid": 1, "autoname": "field:period_name",
"engine": "InnoDB", "beta": 0,
"field_order": [ "creation": "2018-04-13 18:50:14.672323",
"period_name", "custom": 0,
"start_date", "docstatus": 0,
"end_date", "doctype": "DocType",
"column_break_4", "document_type": "",
"company", "editable_grid": 1,
"section_break_7", "engine": "InnoDB",
"closed_documents"
],
"fields": [ "fields": [
{ {
"fieldname": "period_name", "allow_bulk_edit": 0,
"fieldtype": "Data", "allow_on_submit": 0,
"in_list_view": 1, "bold": 0,
"label": "Period Name", "collapsible": 0,
"reqd": 1, "columns": 0,
"unique": 1 "fieldname": "period_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Period Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "start_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": "Start 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,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "end_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": "End Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
}, },
{ {
"fieldname": "start_date", "allow_bulk_edit": 0,
"fieldtype": "Date", "allow_on_submit": 0,
"in_list_view": 1, "bold": 0,
"label": "Start Date", "collapsible": 0,
"reqd": 1 "columns": 0,
}, "fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"fieldname": "end_date", "allow_bulk_edit": 0,
"fieldtype": "Date", "allow_on_submit": 0,
"in_list_view": 1, "bold": 0,
"label": "End Date", "collapsible": 0,
"reqd": 1 "columns": 0,
}, "fieldname": "closed_documents",
{ "fieldtype": "Table",
"fieldname": "column_break_4", "hidden": 0,
"fieldtype": "Column Break" "ignore_user_permissions": 0,
}, "ignore_xss_filter": 0,
{ "in_filter": 0,
"fieldname": "company", "in_global_search": 0,
"fieldtype": "Link", "in_list_view": 0,
"in_list_view": 1, "in_standard_filter": 0,
"label": "Company", "label": "Closed Documents",
"options": "Company", "length": 0,
"reqd": 1 "no_copy": 0,
}, "options": "Closed Document",
{ "permlevel": 0,
"fieldname": "section_break_7", "precision": "",
"fieldtype": "Section Break" "print_hide": 0,
}, "print_hide_if_no_value": 0,
{ "read_only": 0,
"fieldname": "closed_documents", "remember_last_selected_value": 0,
"fieldtype": "Table", "report_hide": 0,
"label": "Closed Documents", "reqd": 1,
"options": "Closed Document", "search_index": 0,
"reqd": 1 "set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"links": [], "has_web_view": 0,
"modified": "2024-03-27 13:05:57.388109", "hide_heading": 0,
"modified_by": "Administrator", "hide_toolbar": 0,
"module": "Accounts", "idx": 0,
"name": "Accounting Period", "image_view": 0,
"owner": "Administrator", "in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-08-01 19:14:47.593753",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting Period",
"name_case": "",
"owner": "Administrator",
"permissions": [ "permissions": [
{ {
"create": 1, "amend": 0,
"delete": 1, "cancel": 0,
"email": 1, "create": 1,
"export": 1, "delete": 1,
"print": 1, "email": 1,
"read": 1, "export": 1,
"report": 1, "if_owner": 0,
"role": "System Manager", "import": 0,
"share": 1, "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"create": 1, "amend": 0,
"delete": 1, "cancel": 0,
"email": 1, "create": 1,
"export": 1, "delete": 1,
"print": 1, "email": 1,
"read": 1, "export": 1,
"report": 1, "if_owner": 0,
"role": "Accounts Manager", "import": 0,
"share": 1, "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"create": 1, "amend": 0,
"delete": 1, "cancel": 0,
"email": 1, "create": 1,
"export": 1, "delete": 1,
"print": 1, "email": 1,
"read": 1, "export": 1,
"report": 1, "if_owner": 0,
"role": "Accounts User", "import": 0,
"share": 1, "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"sort_field": "creation", "quick_entry": 0,
"sort_order": "DESC", "read_only": 0,
"states": [], "read_only_onload": 0,
"track_changes": 1 "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
} }

View File

@@ -16,23 +16,6 @@ class ClosedAccountingPeriod(frappe.ValidationError):
class AccountingPeriod(Document): class AccountingPeriod(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.closed_document.closed_document import ClosedDocument
closed_documents: DF.Table[ClosedDocument]
company: DF.Link
end_date: DF.Date
period_name: DF.Data
start_date: DF.Date
# end: auto-generated types
def validate(self): def validate(self):
self.validate_overlap() self.validate_overlap()
@@ -84,10 +67,7 @@ class AccountingPeriod(Document):
for doctype_for_closing in self.get_doctypes_for_closing(): for doctype_for_closing in self.get_doctypes_for_closing():
self.append( self.append(
"closed_documents", "closed_documents",
{ {"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
"document_type": doctype_for_closing.document_type,
"closed": doctype_for_closing.closed,
},
) )
@@ -101,8 +81,6 @@ def validate_accounting_period_on_doc_save(doc, method=None):
date = doc.available_for_use_date date = doc.available_for_use_date
elif doc.doctype == "Asset Repair": elif doc.doctype == "Asset Repair":
date = doc.completion_date date = doc.completion_date
elif doc.doctype == "Period Closing Voucher":
date = doc.period_end_date
else: else:
date = doc.posting_date date = doc.posting_date

View File

@@ -1,9 +1,9 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest import unittest
import frappe import frappe
from frappe.tests import IntegrationTestCase
from frappe.utils import add_months, nowdate from frappe.utils import add_months, nowdate
from erpnext.accounts.doctype.accounting_period.accounting_period import ( from erpnext.accounts.doctype.accounting_period.accounting_period import (
@@ -12,10 +12,10 @@ from erpnext.accounts.doctype.accounting_period.accounting_period import (
) )
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
EXTRA_TEST_RECORD_DEPENDENCIES = ["Item"] test_dependencies = ["Item"]
class TestAccountingPeriod(IntegrationTestCase): class TestAccountingPeriod(unittest.TestCase):
def test_overlap(self): def test_overlap(self):
ap1 = create_accounting_period( ap1 = create_accounting_period(
start_date="2018-04-01", end_date="2018-06-30", company="Wind Power LLC" start_date="2018-04-01", end_date="2018-06-30", company="Wind Power LLC"
@@ -34,7 +34,9 @@ class TestAccountingPeriod(IntegrationTestCase):
ap1 = create_accounting_period(period_name="Test Accounting Period 2") ap1 = create_accounting_period(period_name="Test Accounting Period 2")
ap1.save() ap1.save()
doc = create_sales_invoice(do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC") doc = create_sales_invoice(
do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
)
self.assertRaises(ClosedAccountingPeriod, doc.save) self.assertRaises(ClosedAccountingPeriod, doc.save)
def tearDown(self): def tearDown(self):

View File

@@ -1,25 +1,8 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on("Accounts Settings", { frappe.ui.form.on('Accounts Settings', {
refresh: function (frm) {}, refresh: function(frm) {
enable_immutable_ledger: function (frm) {
if (!frm.doc.enable_immutable_ledger) {
return;
}
let msg = __("Enabling this will change the way how cancelled transactions are handled."); }
msg += " ";
msg += __("Please enable only if the understand the effects of enabling this.");
msg += "<br>";
msg += "Do you still want to enable immutable ledger?";
frappe.confirm(
msg,
() => {},
() => {
frm.set_value("enable_immutable_ledger", 0);
}
);
},
}); });

View File

@@ -1,6 +1,7 @@
{ {
"actions": [], "actions": [],
"creation": "2013-06-24 15:49:57", "creation": "2013-06-24 15:49:57",
"description": "Settings for Accounts",
"doctype": "DocType", "doctype": "DocType",
"document_type": "Other", "document_type": "Other",
"editable_grid": 1, "editable_grid": 1,
@@ -12,7 +13,6 @@
"unlink_advance_payment_on_cancelation_of_order", "unlink_advance_payment_on_cancelation_of_order",
"column_break_13", "column_break_13",
"delete_linked_ledger_entries", "delete_linked_ledger_entries",
"enable_immutable_ledger",
"invoicing_features_section", "invoicing_features_section",
"check_supplier_invoice_uniqueness", "check_supplier_invoice_uniqueness",
"automatically_fetch_payment_terms", "automatically_fetch_payment_terms",
@@ -55,8 +55,6 @@
"post_change_gl_entries", "post_change_gl_entries",
"assets_tab", "assets_tab",
"asset_settings_section", "asset_settings_section",
"calculate_depr_using_total_days",
"column_break_gjcc",
"book_asset_depreciation_entry_automatically", "book_asset_depreciation_entry_automatically",
"closing_settings_tab", "closing_settings_tab",
"period_closing_settings_section", "period_closing_settings_section",
@@ -68,14 +66,7 @@
"show_balance_in_coa", "show_balance_in_coa",
"banking_tab", "banking_tab",
"enable_party_matching", "enable_party_matching",
"enable_fuzzy_matching", "enable_fuzzy_matching"
"reports_tab",
"remarks_section",
"general_ledger_remarks_length",
"column_break_lvjk",
"receivable_payable_remarks_length",
"payment_request_settings",
"create_pr_in_draft_status"
], ],
"fields": [ "fields": [
{ {
@@ -110,7 +101,7 @@
}, },
{ {
"default": "0", "default": "0",
"description": "Enabling this ensures each Purchase Invoice has a unique value in Supplier Invoice No. field within a particular fiscal year", "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"
@@ -431,64 +422,6 @@
"fieldname": "round_row_wise_tax", "fieldname": "round_row_wise_tax",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Round Tax Amount Row-wise" "label": "Round Tax Amount Row-wise"
},
{
"fieldname": "reports_tab",
"fieldtype": "Tab Break",
"label": "Reports"
},
{
"default": "0",
"description": "Truncates 'Remarks' column to set character length",
"fieldname": "general_ledger_remarks_length",
"fieldtype": "Int",
"label": "General Ledger"
},
{
"default": "0",
"description": "Truncates 'Remarks' column to set character length",
"fieldname": "receivable_payable_remarks_length",
"fieldtype": "Int",
"label": "Accounts Receivable/Payable"
},
{
"fieldname": "column_break_lvjk",
"fieldtype": "Column Break"
},
{
"fieldname": "remarks_section",
"fieldtype": "Section Break",
"label": "Remarks Column Length"
},
{
"default": "0",
"description": "On enabling this cancellation entries will be posted on the actual cancellation date and reports will consider cancelled entries as well",
"fieldname": "enable_immutable_ledger",
"fieldtype": "Check",
"label": "Enable Immutable Ledger"
},
{
"fieldname": "column_break_gjcc",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "Enable this option to calculate daily depreciation by considering the total number of days in the entire depreciation period, (including leap years) while using daily pro-rata based depreciation",
"fieldname": "calculate_depr_using_total_days",
"fieldtype": "Check",
"label": "Calculate daily depreciation using total days in depreciation period"
},
{
"description": "Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.",
"fieldname": "payment_request_settings",
"fieldtype": "Tab Break",
"label": "Payment Request"
},
{
"default": "1",
"fieldname": "create_pr_in_draft_status",
"fieldtype": "Check",
"label": "Create in Draft Status"
} }
], ],
"icon": "icon-cog", "icon": "icon-cog",
@@ -496,7 +429,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2024-07-26 06:48:52.714630", "modified": "2023-08-28 00:12:02.740633",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",
@@ -521,7 +454,7 @@
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "creation", "sort_field": "modified",
"sort_order": "ASC", "sort_order": "ASC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1

View File

@@ -14,55 +14,6 @@ from erpnext.stock.utils import check_pending_reposting
class AccountsSettings(Document): class AccountsSettings(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
acc_frozen_upto: DF.Date | None
add_taxes_from_item_tax_template: DF.Check
allow_multi_currency_invoices_against_single_party_account: DF.Check
allow_stale: DF.Check
auto_reconcile_payments: DF.Check
automatically_fetch_payment_terms: DF.Check
automatically_process_deferred_accounting_entry: DF.Check
book_asset_depreciation_entry_automatically: DF.Check
book_deferred_entries_based_on: DF.Literal["Days", "Months"]
book_deferred_entries_via_journal_entry: DF.Check
book_tax_discount_loss: DF.Check
calculate_depr_using_total_days: DF.Check
check_supplier_invoice_uniqueness: DF.Check
create_pr_in_draft_status: DF.Check
credit_controller: DF.Link | None
delete_linked_ledger_entries: DF.Check
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
enable_common_party_accounting: DF.Check
enable_fuzzy_matching: DF.Check
enable_immutable_ledger: DF.Check
enable_party_matching: DF.Check
frozen_accounts_modifier: DF.Link | None
general_ledger_remarks_length: DF.Int
ignore_account_closing_balance: DF.Check
make_payment_via_journal_entry: DF.Check
merge_similar_account_heads: DF.Check
over_billing_allowance: DF.Currency
post_change_gl_entries: DF.Check
receivable_payable_remarks_length: DF.Int
role_allowed_to_over_bill: DF.Link | None
round_row_wise_tax: DF.Check
show_balance_in_coa: DF.Check
show_inclusive_tax_in_print: DF.Check
show_payment_schedule_in_print: DF.Check
show_taxes_as_table_in_print: DF.Check
stale_days: DF.Int
submit_journal_entries: DF.Check
unlink_advance_payment_on_cancelation_of_order: DF.Check
unlink_payment_on_cancellation_of_invoice: DF.Check
# end: auto-generated types
def validate(self): def validate(self):
old_doc = self.get_doc_before_save() old_doc = self.get_doc_before_save()
clear_cache = False clear_cache = False

View File

@@ -1,11 +1,8 @@
frappe.ui.form.on("Accounts Settings", {
refresh: function (frm) { frappe.ui.form.on('Accounts Settings', {
refresh: function(frm) {
frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through"); frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through");
frm.set_df_property( frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
"frozen_accounts_modifier",
"label",
"Role Allowed to Close Books & Make Changes to Closed Periods"
);
frm.set_df_property("credit_controller", "label", "Credit Manager"); frm.set_df_property("credit_controller", "label", "Credit Manager");
}, }
}); });

View File

@@ -1,10 +1,9 @@
import unittest import unittest
import frappe import frappe
from frappe.tests import IntegrationTestCase
class TestAccountsSettings(IntegrationTestCase): class TestAccountsSettings(unittest.TestCase):
def tearDown(self): def tearDown(self):
# Just in case `save` method succeeds, we need to take things back to default so that other tests # Just in case `save` method succeeds, we need to take things back to default so that other tests
# don't break # don't break

View File

@@ -1,8 +0,0 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Advance Payment Ledger Entry", {
// refresh(frm) {
// },
// });

View File

@@ -1,113 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2024-10-16 16:57:12.085072",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"company",
"voucher_type",
"voucher_no",
"against_voucher_type",
"against_voucher_no",
"amount",
"currency",
"event"
],
"fields": [
{
"fieldname": "voucher_type",
"fieldtype": "Link",
"label": "Voucher Type",
"options": "DocType",
"read_only": 1
},
{
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"label": "Voucher No",
"options": "voucher_type",
"read_only": 1
},
{
"fieldname": "against_voucher_type",
"fieldtype": "Link",
"label": "Against Voucher Type",
"options": "DocType",
"read_only": 1
},
{
"fieldname": "against_voucher_no",
"fieldtype": "Dynamic Link",
"label": "Against Voucher No",
"options": "against_voucher_type",
"read_only": 1
},
{
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount",
"read_only": 1
},
{
"fieldname": "currency",
"fieldtype": "Link",
"label": "Currency",
"options": "Currency",
"read_only": 1
},
{
"fieldname": "event",
"fieldtype": "Data",
"label": "Event",
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-11-05 10:31:28.736671",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Advance Payment Ledger Entry",
"owner": "Administrator",
"permissions": [
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Auditor",
"share": 1
}
],
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -1,27 +0,0 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class AdvancePaymentLedgerEntry(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
against_voucher_no: DF.DynamicLink | None
against_voucher_type: DF.Link | None
amount: DF.Currency
company: DF.Link | None
currency: DF.Link | None
event: DF.Data | None
voucher_no: DF.DynamicLink | None
voucher_type: DF.Link | None
# end: auto-generated types
pass

View File

@@ -1,228 +0,0 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
from frappe.tests import IntegrationTestCase, UnitTestCase
from frappe.utils import nowdate, today
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
# On IntegrationTestCase, the doctype test records and all
# link-field test record depdendencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class TestAdvancePaymentLedgerEntry(AccountsTestMixin, IntegrationTestCase):
"""
Integration tests for AdvancePaymentLedgerEntry.
Use this class for testing interactions between multiple components.
"""
def setUp(self):
self.create_company()
self.create_usd_receivable_account()
self.create_usd_payable_account()
self.create_item()
self.clear_old_entries()
def tearDown(self):
frappe.db.rollback()
def create_sales_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
"""
Helper method
"""
so = make_sales_order(
company=self.company,
customer=self.customer,
currency=currency,
item=self.item,
qty=qty,
rate=rate,
transaction_date=today(),
do_not_submit=do_not_submit,
)
return so
def create_purchase_order(self, qty=1, rate=100, currency="INR", do_not_submit=False):
"""
Helper method
"""
po = create_purchase_order(
company=self.company,
customer=self.supplier,
currency=currency,
item=self.item,
qty=qty,
rate=rate,
transaction_date=today(),
do_not_submit=do_not_submit,
)
return po
def test_so_advance_paid_and_currency_with_payment(self):
self.create_customer("_Test USD Customer", "USD")
so = self.create_sales_order(currency="USD", do_not_submit=True)
so.conversion_rate = 80
so.submit()
pe_exchange_rate = 85
pe = get_payment_entry(so.doctype, so.name, bank_account=self.cash)
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_from = self.debtors_usd
pe.paid_from_account_currency = "USD"
pe.source_exchange_rate = pe_exchange_rate
pe.paid_amount = so.grand_total
pe.received_amount = pe_exchange_rate * pe.paid_amount
pe.references[0].outstanding_amount = 100
pe.references[0].total_amount = 100
pe.references[0].allocated_amount = 100
pe.save().submit()
so.reload()
self.assertEqual(so.advance_paid, 100)
self.assertEqual(so.party_account_currency, "USD")
# cancel advance payment
pe.reload()
pe.cancel()
so.reload()
self.assertEqual(so.advance_paid, 0)
self.assertEqual(so.party_account_currency, "USD")
def test_so_advance_paid_and_currency_with_journal(self):
self.create_customer("_Test USD Customer", "USD")
so = self.create_sales_order(currency="USD", do_not_submit=True)
so.conversion_rate = 80
so.submit()
je_exchange_rate = 85
je = frappe.get_doc(
{
"doctype": "Journal Entry",
"company": self.company,
"voucher_type": "Journal Entry",
"posting_date": so.transaction_date,
"multi_currency": True,
"accounts": [
{
"account": self.debtors_usd,
"party_type": "Customer",
"party": so.customer,
"credit": 8500,
"credit_in_account_currency": 100,
"is_advance": "Yes",
"reference_type": so.doctype,
"reference_name": so.name,
"exchange_rate": je_exchange_rate,
},
{
"account": self.cash,
"debit": 8500,
"debit_in_account_currency": 8500,
},
],
}
)
je.save().submit()
so.reload()
self.assertEqual(so.advance_paid, 100)
self.assertEqual(so.party_account_currency, "USD")
# cancel advance payment
je.reload()
je.cancel()
so.reload()
self.assertEqual(so.advance_paid, 0)
self.assertEqual(so.party_account_currency, "USD")
def test_po_advance_paid_and_currency_with_payment(self):
self.create_supplier("_Test USD Supplier", "USD")
po = self.create_purchase_order(currency="USD", do_not_submit=True)
po.conversion_rate = 80
po.submit()
pe_exchange_rate = 85
pe = get_payment_entry(po.doctype, po.name, bank_account=self.cash)
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_to = self.creditors_usd
pe.paid_to_account_currency = "USD"
pe.target_exchange_rate = pe_exchange_rate
pe.received_amount = po.grand_total
pe.paid_amount = pe_exchange_rate * pe.received_amount
pe.references[0].outstanding_amount = 100
pe.references[0].total_amount = 100
pe.references[0].allocated_amount = 100
pe.save().submit()
po.reload()
self.assertEqual(po.advance_paid, 100)
self.assertEqual(po.party_account_currency, "USD")
# cancel advance payment
pe.reload()
pe.cancel()
po.reload()
self.assertEqual(po.advance_paid, 0)
self.assertEqual(po.party_account_currency, "USD")
def test_po_advance_paid_and_currency_with_journal(self):
self.create_supplier("_Test USD Supplier", "USD")
po = self.create_purchase_order(currency="USD", do_not_submit=True)
po.conversion_rate = 80
po.submit()
je_exchange_rate = 85
je = frappe.get_doc(
{
"doctype": "Journal Entry",
"company": self.company,
"voucher_type": "Journal Entry",
"posting_date": po.transaction_date,
"multi_currency": True,
"accounts": [
{
"account": self.creditors_usd,
"party_type": "Supplier",
"party": po.supplier,
"debit": 8500,
"debit_in_account_currency": 100,
"is_advance": "Yes",
"reference_type": po.doctype,
"reference_name": po.name,
"exchange_rate": je_exchange_rate,
},
{
"account": self.cash,
"credit": 8500,
"credit_in_account_currency": 8500,
},
],
}
)
je.save().submit()
po.reload()
self.assertEqual(po.advance_paid, 100)
self.assertEqual(po.party_account_currency, "USD")
# cancel advance payment
je.reload()
je.cancel()
po.reload()
self.assertEqual(po.advance_paid, 0)
self.assertEqual(po.party_account_currency, "USD")

View File

@@ -45,13 +45,12 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:05:58.308002", "modified": "2021-11-25 10:27:51.712286",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Advance Tax", "name": "Advance Tax",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC"
"states": []
} }

View File

@@ -6,22 +6,4 @@ from frappe.model.document import Document
class AdvanceTax(Document): class AdvanceTax(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
account_head: DF.Link | None
allocated_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
reference_detail: DF.Data | None
reference_name: DF.DynamicLink | None
reference_type: DF.Link | None
# end: auto-generated types
pass pass

View File

@@ -13,7 +13,6 @@
"col_break_1", "col_break_1",
"description", "description",
"included_in_paid_amount", "included_in_paid_amount",
"set_by_item_tax_template",
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
@@ -21,13 +20,11 @@
"rate", "rate",
"section_break_9", "section_break_9",
"currency", "currency",
"net_amount",
"tax_amount", "tax_amount",
"total", "total",
"allocated_amount", "allocated_amount",
"column_break_13", "column_break_13",
"base_tax_amount", "base_tax_amount",
"base_net_amount",
"base_total" "base_total"
], ],
"fields": [ "fields": [
@@ -177,46 +174,17 @@
"label": "Account Currency", "label": "Account Currency",
"options": "Currency", "options": "Currency",
"read_only": 1 "read_only": 1
},
{
"columns": 2,
"fieldname": "net_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Net Amount",
"options": "currency",
"read_only": 1
},
{
"fieldname": "base_net_amount",
"fieldtype": "Currency",
"label": "Net Amount (Company Currency)",
"oldfieldname": "tax_amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"default": "0",
"fieldname": "set_by_item_tax_template",
"fieldtype": "Check",
"hidden": 1,
"label": "Set by Item Tax Template",
"print_hide": 1,
"read_only": 1,
"report_hide": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-11-22 19:16:22.346267", "modified": "2021-11-25 11:10:10.945027",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Advance Taxes and Charges", "name": "Advance Taxes and Charges",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"sort_field": "creation", "sort_field": "modified",
"sort_order": "ASC", "sort_order": "ASC"
"states": []
} }

View File

@@ -7,36 +7,4 @@ from frappe.model.document import Document
class AdvanceTaxesandCharges(Document): class AdvanceTaxesandCharges(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
account_head: DF.Link
add_deduct_tax: DF.Literal["Add", "Deduct"]
allocated_amount: DF.Currency
base_net_amount: DF.Currency
base_tax_amount: DF.Currency
base_total: DF.Currency
charge_type: DF.Literal[
"", "Actual", "On Paid Amount", "On Previous Row Amount", "On Previous Row Total"
]
cost_center: DF.Link | None
currency: DF.Link | None
description: DF.SmallText
included_in_paid_amount: DF.Check
net_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
rate: DF.Float
row_id: DF.Data | None
set_by_item_tax_template: DF.Check
tax_amount: DF.Currency
total: DF.Currency
# end: auto-generated types
pass pass

View File

@@ -14,27 +14,30 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Accounting Dimension", "label": "Accounting Dimension",
"options": "DocType", "options": "DocType",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "dimension_value", "fieldname": "dimension_value",
"fieldtype": "Dynamic Link", "fieldtype": "Dynamic Link",
"in_list_view": 1, "in_list_view": 1,
"options": "accounting_dimension" "options": "accounting_dimension",
"show_days": 1,
"show_seconds": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:05:58.587487", "modified": "2020-11-23 09:56:19.744200",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Allowed Dimension", "name": "Allowed Dimension",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -7,19 +7,4 @@ from frappe.model.document import Document
class AllowedDimension(Document): class AllowedDimension(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
accounting_dimension: DF.Link | None
dimension_value: DF.DynamicLink | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass pass

View File

@@ -11,7 +11,6 @@
{ {
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Company", "label": "Company",
"options": "Company", "options": "Company",
@@ -20,15 +19,14 @@
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:05:58.698893", "modified": "2020-05-01 12:32:34.044911",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Allowed To Transact With", "name": "Allowed To Transact With",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -6,18 +6,4 @@ from frappe.model.document import Document
class AllowedToTransactWith(Document): class AllowedToTransactWith(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
company: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass pass

View File

@@ -15,7 +15,9 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Accounts", "label": "Accounts",
"options": "Account", "options": "Account",
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"columns": 2, "columns": 2,
@@ -23,21 +25,22 @@
"fieldname": "is_mandatory", "fieldname": "is_mandatory",
"fieldtype": "Check", "fieldtype": "Check",
"in_list_view": 1, "in_list_view": 1,
"label": "Is Mandatory" "label": "Is Mandatory",
"show_days": 1,
"show_seconds": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:05:59.168897", "modified": "2020-11-22 19:55:13.324136",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Applicable On Account", "name": "Applicable On Account",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -7,19 +7,4 @@ from frappe.model.document import Document
class ApplicableOnAccount(Document): class ApplicableOnAccount(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
applicable_on_account: DF.Link
is_mandatory: DF.Check
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass pass

View File

@@ -1,36 +1,38 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.provide("erpnext.integrations"); frappe.provide('erpnext.integrations');
frappe.ui.form.on("Bank", { frappe.ui.form.on('Bank', {
onload: function (frm) { onload: function(frm) {
add_fields_to_mapping_table(frm); add_fields_to_mapping_table(frm);
}, },
refresh: function (frm) { refresh: function(frm) {
add_fields_to_mapping_table(frm); add_fields_to_mapping_table(frm);
frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal); frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if (frm.doc.__islocal) { if (frm.doc.__islocal) {
frm.set_df_property("address_and_contact", "hidden", 1); frm.set_df_property('address_and_contact', 'hidden', 1);
frappe.contacts.clear_address_and_contact(frm); frappe.contacts.clear_address_and_contact(frm);
} else { }
frm.set_df_property("address_and_contact", "hidden", 0); else {
frm.set_df_property('address_and_contact', 'hidden', 0);
frappe.contacts.render_address_and_contact(frm); frappe.contacts.render_address_and_contact(frm);
} }
if (frm.doc.plaid_access_token) { if (frm.doc.plaid_access_token) {
frm.add_custom_button(__("Refresh Plaid Link"), () => { frm.add_custom_button(__('Refresh Plaid Link'), () => {
new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token); new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
}); });
} }
}, }
}); });
let add_fields_to_mapping_table = function (frm) { let add_fields_to_mapping_table = function (frm) {
let options = []; let options = [];
frappe.model.with_doctype("Bank Transaction", function () { frappe.model.with_doctype("Bank Transaction", function() {
let meta = frappe.get_meta("Bank Transaction"); let meta = frappe.get_meta("Bank Transaction");
meta.fields.forEach((value) => { meta.fields.forEach(value => {
if (!["Section Break", "Column Break"].includes(value.fieldtype)) { if (!["Section Break", "Column Break"].includes(value.fieldtype)) {
options.push(value.fieldname); options.push(value.fieldname);
} }
@@ -38,32 +40,30 @@ let add_fields_to_mapping_table = function (frm) {
}); });
frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property( frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
"bank_transaction_field", 'bank_transaction_field', 'options', options
"options",
options
); );
}; };
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink { erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
constructor(access_token) { constructor(access_token) {
this.access_token = access_token; this.access_token = access_token;
this.plaidUrl = "https://cdn.plaid.com/link/v2/stable/link-initialize.js"; this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
this.init_config(); this.init_config();
} }
async init_config() { async init_config() {
this.plaid_env = await frappe.db.get_single_value("Plaid Settings", "plaid_env"); this.plaid_env = await frappe.db.get_single_value('Plaid Settings', 'plaid_env');
this.token = await this.get_link_token_for_update(); this.token = await this.get_link_token_for_update();
this.init_plaid(); this.init_plaid();
} }
async get_link_token_for_update() { async get_link_token_for_update() {
const token = frappe.xcall( const token = frappe.xcall(
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update", 'erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update',
{ access_token: this.access_token } { access_token: this.access_token }
); )
if (!token) { if (!token) {
frappe.throw(__("Cannot retrieve link token for update. Check Error Log for more information")); frappe.throw(__('Cannot retrieve link token for update. Check Error Log for more information'));
} }
return token; return token;
} }
@@ -90,45 +90,35 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
resolve(); resolve();
return; return;
} }
const el = document.createElement("script"); const el = document.createElement('script');
el.type = "text/javascript"; el.type = 'text/javascript';
el.async = true; el.async = true;
el.src = src; el.src = src;
el.addEventListener("load", resolve); el.addEventListener('load', resolve);
el.addEventListener("error", reject); el.addEventListener('error', reject);
el.addEventListener("abort", reject); el.addEventListener('abort', reject);
document.head.appendChild(el); document.head.appendChild(el);
}); });
} }
onScriptLoaded(me) { onScriptLoaded(me) {
me.linkHandler = Plaid.create({ me.linkHandler = Plaid.create({ // eslint-disable-line no-undef
// eslint-disable-line no-undef
env: me.plaid_env, env: me.plaid_env,
token: me.token, token: me.token,
onSuccess: me.plaid_success, onSuccess: me.plaid_success
}); });
} }
onScriptError(error) { onScriptError(error) {
frappe.msgprint( frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
__(
"There was an issue connecting to Plaid's authentication server. Check browser console for more information"
)
);
console.log(error); console.log(error);
} }
plaid_success(token, response) { plaid_success(token, response) {
frappe frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids', {
.xcall( response: response,
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids", }).then(() => {
{ frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
response: response, });
}
)
.then(() => {
frappe.show_alert({ message: __("Plaid Link Updated"), indicator: "green" });
});
} }
}; };

View File

@@ -101,7 +101,7 @@
} }
], ],
"links": [], "links": [],
"modified": "2024-03-27 13:06:36.896195", "modified": "2020-07-17 14:00:13.105433",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank", "name": "Bank",
@@ -121,8 +121,7 @@
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -10,25 +10,6 @@ from frappe.model.document import Document
class Bank(Document): class Bank(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.bank_transaction_mapping.bank_transaction_mapping import (
BankTransactionMapping,
)
bank_name: DF.Data
bank_transaction_mapping: DF.Table[BankTransactionMapping]
plaid_access_token: DF.Data | None
swift_number: DF.Data | None
website: DF.Data | None
# end: auto-generated types
def onload(self): def onload(self):
"""Load address and contacts in `__onload`""" """Load address and contacts in `__onload`"""
load_address_and_contact(self) load_address_and_contact(self)

View File

@@ -1,9 +1,8 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest import unittest
from frappe.tests import IntegrationTestCase
class TestBank(unittest.TestCase):
class TestBank(IntegrationTestCase):
pass pass

View File

@@ -1,49 +1,45 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on("Bank Account", { frappe.ui.form.on('Bank Account', {
setup: function (frm) { setup: function(frm) {
frm.set_query("account", function () { frm.set_query("account", function() {
return { return {
filters: { filters: {
account_type: "Bank", 'account_type': 'Bank',
company: frm.doc.company, 'company': frm.doc.company,
is_group: 0, 'is_group': 0
}, }
}; };
}); });
frm.set_query("party_type", function () { frm.set_query("party_type", function() {
return { return {
query: "erpnext.setup.doctype.party_type.party_type.get_party_type", query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
}; };
}); });
}, },
refresh: function (frm) { refresh: function(frm) {
frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Bank Account" }; frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank Account' }
frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal); frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if (frm.doc.__islocal) { if (frm.doc.__islocal) {
frappe.contacts.clear_address_and_contact(frm); frappe.contacts.clear_address_and_contact(frm);
} else { }
else {
frappe.contacts.render_address_and_contact(frm); frappe.contacts.render_address_and_contact(frm);
} }
if (frm.doc.integration_id) { if (frm.doc.integration_id) {
frm.add_custom_button(__("Unlink external integrations"), function () { frm.add_custom_button(__("Unlink external integrations"), function() {
frappe.confirm( frappe.confirm(__("This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"), function() {
__( frm.set_value("integration_id", "");
"This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?" });
),
function () {
frm.set_value("integration_id", "");
}
);
}); });
} }
}, },
is_company_account: function (frm) { is_company_account: function(frm) {
frm.set_df_property("account", "reqd", frm.doc.is_company_account); frm.set_df_property('account', 'reqd', frm.doc.is_company_account);
}, }
}); });

View File

@@ -208,49 +208,8 @@
"label": "Disabled" "label": "Disabled"
} }
], ],
"links": [ "links": [],
{ "modified": "2023-09-22 21:31:34.763977",
"group": "Transactions",
"link_doctype": "Payment Request",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Payment Order",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Bank Guarantee",
"link_fieldname": "bank_account"
},
{
"group": "Transactions",
"link_doctype": "Bank Transaction",
"link_fieldname": "bank_account"
},
{
"group": "Accounting",
"link_doctype": "Payment Entry",
"link_fieldname": "bank_account"
},
{
"group": "Accounting",
"link_doctype": "Journal Entry",
"link_fieldname": "bank_account"
},
{
"group": "Party",
"link_doctype": "Customer",
"link_fieldname": "default_bank_account"
},
{
"group": "Party",
"link_doctype": "Supplier",
"link_fieldname": "default_bank_account"
}
],
"modified": "2024-10-30 09:41:14.113414",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Account", "name": "Bank Account",
@@ -283,7 +242,7 @@
} }
], ],
"search_fields": "bank,account", "search_fields": "bank,account",
"sort_field": "creation", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1

View File

@@ -9,37 +9,9 @@ from frappe.contacts.address_and_contact import (
load_address_and_contact, load_address_and_contact,
) )
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import comma_and, get_link_to_form
class BankAccount(Document): class BankAccount(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
account: DF.Link | None
account_name: DF.Data
account_subtype: DF.Link | None
account_type: DF.Link | None
bank: DF.Link
bank_account_no: DF.Data | None
branch_code: DF.Data | None
company: DF.Link | None
disabled: DF.Check
iban: DF.Data | None
integration_id: DF.Data | None
is_company_account: DF.Check
is_default: DF.Check
last_integration_date: DF.Date | None
mask: DF.Data | None
party: DF.DynamicLink | None
party_type: DF.Link | None
# end: auto-generated types
def onload(self): def onload(self):
"""Load address and contacts in `__onload`""" """Load address and contacts in `__onload`"""
load_address_and_contact(self) load_address_and_contact(self)
@@ -53,24 +25,10 @@ class BankAccount(Document):
def validate(self): def validate(self):
self.validate_company() self.validate_company()
self.validate_iban() self.validate_iban()
self.validate_account()
self.update_default_bank_account()
def validate_account(self):
if self.account:
if accounts := frappe.db.get_all(
"Bank Account", filters={"account": self.account, "name": ["!=", self.name]}, as_list=1
):
frappe.throw(
_("'{0}' account is already used by {1}. Use another account.").format(
frappe.bold(self.account),
frappe.bold(comma_and([get_link_to_form(self.doctype, x[0]) for x in accounts])),
)
)
def validate_company(self): def validate_company(self):
if self.is_company_account and not self.company: if self.is_company_account and not self.company:
frappe.throw(_("Company is mandatory for company account")) frappe.throw(_("Company is manadatory for company account"))
def validate_iban(self): def validate_iban(self):
""" """
@@ -101,51 +59,19 @@ class BankAccount(Document):
if to_check % 97 != 1: if to_check % 97 != 1:
frappe.throw(_("IBAN is not valid")) frappe.throw(_("IBAN is not valid"))
def update_default_bank_account(self):
if self.is_default and not self.disabled:
frappe.db.set_value(
"Bank Account",
{
"party_type": self.party_type,
"party": self.party,
"is_company_account": self.is_company_account,
"is_default": 1,
"disabled": 0,
},
"is_default",
0,
)
@frappe.whitelist() @frappe.whitelist()
def make_bank_account(doctype, docname): def make_bank_account(doctype, docname):
doc = frappe.new_doc("Bank Account") doc = frappe.new_doc("Bank Account")
doc.party_type = doctype doc.party_type = doctype
doc.party = docname doc.party = docname
doc.is_default = 1
return doc return doc
def get_party_bank_account(party_type, party): def get_party_bank_account(party_type, party):
return frappe.db.get_value( return frappe.db.get_value(party_type, party, "default_bank_account")
"Bank Account",
{"party_type": party_type, "party": party, "is_default": 1, "disabled": 0},
"name",
)
def get_default_company_bank_account(company, party_type, party):
default_company_bank_account = frappe.db.get_value(party_type, party, "default_bank_account")
if default_company_bank_account:
if company != frappe.get_cached_value("Bank Account", default_company_bank_account, "company"):
default_company_bank_account = None
if not default_company_bank_account:
default_company_bank_account = frappe.db.get_value(
"Bank Account", {"company": company, "is_company_account": 1, "is_default": 1}
)
return default_company_bank_account
@frappe.whitelist() @frappe.whitelist()

View File

@@ -0,0 +1,20 @@
from frappe import _
def get_data():
return {
"fieldname": "bank_account",
"non_standard_fieldnames": {
"Customer": "default_bank_account",
"Supplier": "default_bank_account",
},
"transactions": [
{
"label": _("Payments"),
"items": ["Payment Entry", "Payment Request", "Payment Order", "Payroll Entry"],
},
{"label": _("Party"), "items": ["Customer", "Supplier"]},
{"items": ["Bank Guarantee"]},
{"items": ["Journal Entry"]},
],
}

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