Merge branch 'frappe:develop' into prevent-set_payment_schedule-si-return

This commit is contained in:
Diógenes Souza
2024-12-03 17:51:03 -03:00
committed by GitHub
1079 changed files with 519947 additions and 670157 deletions

View File

@@ -18,4 +18,4 @@ max_line_length = 110
[{*.json}] [{*.json}]
insert_final_newline = false insert_final_newline = false
indent_style = space indent_style = space
indent_size = 2 indent_size = 1

View File

@@ -38,3 +38,7 @@ ec74a5e56617bbd76ac402451468fd4668af543d
# ruff formatting # ruff formatting
a308792ee7fda18a681e9181f4fd00b36385bc23 a308792ee7fda18a681e9181f4fd00b36385bc23
# noisy typing refactoring of get_item_details
7b7211ac79c248a79ba8a999ff34e734d874c0ae
d827ed21adc7b36047e247cbb0dc6388d048a7f9

View File

@@ -12,9 +12,16 @@ pip install frappe-bench
githubbranch=${GITHUB_BASE_REF:-${GITHUB_REF##*/}} githubbranch=${GITHUB_BASE_REF:-${GITHUB_REF##*/}}
frappeuser=${FRAPPE_USER:-"frappe"} frappeuser=${FRAPPE_USER:-"frappe"}
frappebranch=${FRAPPE_BRANCH:-$githubbranch} frappecommitish=${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

@@ -37,4 +37,4 @@ gh auth setup-git
git push -u upstream "${branch_name}" git push -u upstream "${branch_name}"
echo "Creating a PR..." echo "Creating a PR..."
gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" -R frappe/erpnext gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" --reviewer ${PR_REVIEWER} -R frappe/erpnext

8
.github/stale.yml vendored
View File

@@ -12,6 +12,14 @@ 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

@@ -36,3 +36,4 @@ jobs:
env: env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
BASE_BRANCH: ${{ matrix.branch }} BASE_BRANCH: ${{ matrix.branch }}
PR_REVIEWER: barredterra # change to your GitHub username if you copied this file

View File

@@ -137,7 +137,8 @@ jobs:
update_to_version 15 update_to_version 15
echo "Updating to latest version" echo "Updating to latest version"
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" git -C "apps/frappe" fetch --depth 1 upstream "${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

@@ -0,0 +1,130 @@
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,6 +1,8 @@
name: Server (Mariadb) name: Server (Mariadb)
on: on:
repository_dispatch:
types: [frappe-framework-change]
pull_request: pull_request:
paths-ignore: paths-ignore:
- '**.js' - '**.js'
@@ -117,10 +119,10 @@ 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.inputs.branch }} FRAPPE_BRANCH: ${{ github.event.client_payload.sha || 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 4 --build-number ${{ matrix.container }}' run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --total-builds ${{ strategy.job-total }} --build-number ${{ matrix.container }}'
env: env:
TYPE: server TYPE: server
CAPTURE_COVERAGE: ${{ github.event_name != 'pull_request' }} CAPTURE_COVERAGE: ${{ github.event_name != 'pull_request' }}

5
.gitignore vendored
View File

@@ -14,5 +14,8 @@ __pycache__
*~ *~
.idea/ .idea/
.vscode/ .vscode/
.helix/
node_modules/ node_modules/
.backportrc.json .backportrc.json
# Aider AI Chat
.aider*

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

View File

@@ -1 +1,3 @@
**/setup/setup_wizard/data/uom_data.json,erpnext.gettext.extractors.uom_data.extract **/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

@@ -4,5 +4,7 @@ files:
pull_request_title: "fix: sync translations from crowdin" pull_request_title: "fix: sync translations from crowdin"
pull_request_labels: pull_request_labels:
- translation - translation
pull_request_reviewers:
- barredterra # change to your GitHub username if you copied this file
commit_message: "fix: %language% translations" commit_message: "fix: %language% translations"
append_commit_message: false append_commit_message: false

View File

@@ -1,7 +1,10 @@
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__ = "16.0.0-dev"
@@ -149,3 +152,44 @@ 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

@@ -58,7 +58,7 @@ def build_conditions(process_type, account, company):
) )
if account: if account:
conditions += f"AND {deferred_account}='{account}'" conditions += f"AND {deferred_account}={frappe.db.escape(account)}"
elif company: elif company:
conditions += f"AND p.company = {frappe.db.escape(company)}" conditions += f"AND p.company = {frappe.db.escape(company)}"

View File

@@ -124,7 +124,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\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\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"search_index": 1 "search_index": 1
}, },
{ {
@@ -194,7 +194,7 @@
"idx": 1, "idx": 1,
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2024-06-27 16:23:04.444354", "modified": "2024-08-19 15:19:11.095045",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account", "name": "Account",

View File

@@ -60,6 +60,7 @@ class Account(NestedSet):
"Payable", "Payable",
"Receivable", "Receivable",
"Round Off", "Round Off",
"Round Off for Opening",
"Stock", "Stock",
"Stock Adjustment", "Stock Adjustment",
"Stock Received But Not Billed", "Stock Received But Not Billed",
@@ -101,14 +102,12 @@ 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()
validate_field_number("Account", self.name, self.account_number, self.company, "account_number") self.validate_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()
@@ -200,7 +199,7 @@ class Account(NestedSet):
msg = _( msg = _(
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report" "There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
).format( ).format(
frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type frappe.bold(_("Account Type")), doc_before_save.account_type, doc_before_save.account_type
) )
frappe.msgprint(msg) frappe.msgprint(msg)
self.add_comment("Comment", msg) self.add_comment("Comment", msg)
@@ -309,6 +308,22 @@ 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)
@@ -462,19 +477,6 @@ 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)
@@ -515,7 +517,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"))
validate_account_number(name, account_number, account.company) account.validate_account_number(account_number)
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:

View File

@@ -109,7 +109,8 @@
"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"
}, },
@@ -132,7 +133,8 @@
"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

@@ -72,6 +72,7 @@ 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",
}, },
@@ -104,6 +105,7 @@ 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

@@ -1,11 +1,9 @@
# 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.test_runner import make_test_records from frappe.tests import IntegrationTestCase
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.accounts.doctype.account.account import ( from erpnext.accounts.doctype.account.account import (
@@ -15,10 +13,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
test_dependencies = ["Company"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Company"]
class TestAccount(unittest.TestCase): class TestAccount(IntegrationTestCase):
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")
@@ -203,8 +201,6 @@ class TestAccount(unittest.TestCase):
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():
@@ -328,7 +324,7 @@ class TestAccount(unittest.TestCase):
def _make_test_records(verbose=None): def _make_test_records(verbose=None):
from frappe.test_runner import make_test_objects from frappe.tests.utils import make_test_objects
accounts = [ accounts = [
# [account_name, parent_account, is_group] # [account_name, parent_account, is_group]

View File

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

View File

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

View File

@@ -113,9 +113,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, "posting_date": ("<", closing_date)}, filters={"docstatus": 1, "company": company, "period_end_date": ("<", closing_date)},
fields=["name"], fields=["name"],
order_by="posting_date desc", order_by="period_end_date desc",
limit=1, limit=1,
) )

View File

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

View File

@@ -58,7 +58,7 @@ frappe.ui.form.on("Accounting Dimension", {
}, },
label: function (frm) { label: function (frm) {
frm.set_value("fieldname", frappe.model.scrub(frm.doc.label)); frm.set_value("fieldname", frm.doc.label.replace(/ /g, "_").replace(/-/g, "_").toLowerCase());
}, },
document_type: function (frm) { document_type: function (frm) {

View File

@@ -7,6 +7,7 @@ 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
@@ -60,6 +61,7 @@ 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):

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
test_dependencies = ["Cost Center", "Location", "Warehouse", "Department"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Cost Center", "Location", "Warehouse", "Department"]
class TestAccountingDimension(unittest.TestCase): class TestAccountingDimension(IntegrationTestCase):
def setUp(self): def setUp(self):
create_dimension() create_dimension()

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
test_dependencies = ["Location", "Cost Center", "Department"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Location", "Cost Center", "Department"]
class TestAccountingDimensionFilter(unittest.TestCase): class TestAccountingDimensionFilter(unittest.TestCase):

View File

@@ -101,6 +101,8 @@ 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
test_dependencies = ["Item"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Item"]
class TestAccountingPeriod(unittest.TestCase): class TestAccountingPeriod(IntegrationTestCase):
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"

View File

@@ -73,7 +73,9 @@
"remarks_section", "remarks_section",
"general_ledger_remarks_length", "general_ledger_remarks_length",
"column_break_lvjk", "column_break_lvjk",
"receivable_payable_remarks_length" "receivable_payable_remarks_length",
"payment_request_settings",
"create_pr_in_draft_status"
], ],
"fields": [ "fields": [
{ {
@@ -475,6 +477,18 @@
"fieldname": "calculate_depr_using_total_days", "fieldname": "calculate_depr_using_total_days",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Calculate daily depreciation using total days in depreciation period" "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",
@@ -482,7 +496,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2024-07-12 00:24:20.957726", "modified": "2024-07-26 06:48:52.714630",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",

View File

@@ -35,6 +35,7 @@ class AccountsSettings(Document):
book_tax_discount_loss: DF.Check book_tax_discount_loss: DF.Check
calculate_depr_using_total_days: DF.Check calculate_depr_using_total_days: DF.Check
check_supplier_invoice_uniqueness: DF.Check check_supplier_invoice_uniqueness: DF.Check
create_pr_in_draft_status: DF.Check
credit_controller: DF.Link | None credit_controller: DF.Link | None
delete_linked_ledger_entries: DF.Check delete_linked_ledger_entries: DF.Check
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"] determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]

View File

@@ -1,9 +1,10 @@
import unittest import unittest
import frappe import frappe
from frappe.tests import IntegrationTestCase
class TestAccountsSettings(unittest.TestCase): class TestAccountsSettings(IntegrationTestCase):
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

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

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

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

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

@@ -13,6 +13,7 @@
"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",
@@ -20,11 +21,13 @@
"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": [
@@ -174,12 +177,40 @@
"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-03-27 13:05:58.437605", "modified": "2024-11-22 19:16:22.346267",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Advance Taxes and Charges", "name": "Advance Taxes and Charges",

View File

@@ -18,6 +18,7 @@ class AdvanceTaxesandCharges(Document):
account_head: DF.Link account_head: DF.Link
add_deduct_tax: DF.Literal["Add", "Deduct"] add_deduct_tax: DF.Literal["Add", "Deduct"]
allocated_amount: DF.Currency allocated_amount: DF.Currency
base_net_amount: DF.Currency
base_tax_amount: DF.Currency base_tax_amount: DF.Currency
base_total: DF.Currency base_total: DF.Currency
charge_type: DF.Literal[ charge_type: DF.Literal[
@@ -27,11 +28,13 @@ class AdvanceTaxesandCharges(Document):
currency: DF.Link | None currency: DF.Link | None
description: DF.SmallText description: DF.SmallText
included_in_paid_amount: DF.Check included_in_paid_amount: DF.Check
net_amount: DF.Currency
parent: DF.Data parent: DF.Data
parentfield: DF.Data parentfield: DF.Data
parenttype: DF.Data parenttype: DF.Data
rate: DF.Float rate: DF.Float
row_id: DF.Data | None row_id: DF.Data | None
set_by_item_tax_template: DF.Check
tax_amount: DF.Currency tax_amount: DF.Currency
total: DF.Currency total: DF.Currency
# end: auto-generated types # end: auto-generated types

View File

@@ -1,8 +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
from frappe.tests import IntegrationTestCase
class TestBank(unittest.TestCase):
class TestBank(IntegrationTestCase):
pass pass

View File

@@ -208,8 +208,49 @@
"label": "Disabled" "label": "Disabled"
} }
], ],
"links": [], "links": [
"modified": "2024-03-27 13:06:37.049542", {
"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",

View File

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

View File

@@ -1,15 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest import unittest
import frappe import frappe
from frappe import ValidationError from frappe import ValidationError
from frappe.tests import IntegrationTestCase
# test_records = frappe.get_test_records('Bank Account')
class TestBankAccount(unittest.TestCase): class TestBankAccount(IntegrationTestCase):
def test_validate_iban(self): def test_validate_iban(self):
valid_ibans = [ valid_ibans = [
"GB82 WEST 1234 5698 7654 32", "GB82 WEST 1234 5698 7654 32",

View File

@@ -1,8 +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
from frappe.tests import IntegrationTestCase
class TestBankAccountSubtype(unittest.TestCase):
class TestBankAccountSubtype(IntegrationTestCase):
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
# See license.txt # See license.txt
# import frappe # import frappe
import unittest import unittest
from frappe.tests import IntegrationTestCase
class TestBankAccountType(unittest.TestCase):
class TestBankAccountType(IntegrationTestCase):
pass pass

View File

@@ -38,6 +38,11 @@ frappe.ui.form.on("Bank Clearance", {
frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries")); frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries"));
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary"); frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () => frm.trigger("update_clearance_date"));
frm.change_custom_button_type(__("Get Payment Entries"), null, "default");
frm.change_custom_button_type(__("Update Clearance Date"), null, "primary");
}
}, },
update_clearance_date: function (frm) { update_clearance_date: function (frm) {
@@ -45,13 +50,7 @@ frappe.ui.form.on("Bank Clearance", {
method: "update_clearance_date", method: "update_clearance_date",
doc: frm.doc, doc: frm.doc,
callback: function (r, rt) { callback: function (r, rt) {
frm.refresh_field("payment_entries"); frm.refresh();
frm.refresh_fields();
if (!frm.doc.payment_entries.length) {
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
frm.change_custom_button_type(__("Update Clearance Date"), null, "default");
}
}, },
}); });
}, },
@@ -60,17 +59,8 @@ frappe.ui.form.on("Bank Clearance", {
return frappe.call({ return frappe.call({
method: "get_payment_entries", method: "get_payment_entries",
doc: frm.doc, doc: frm.doc,
callback: function (r, rt) { callback: function () {
frm.refresh_field("payment_entries"); frm.refresh();
if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () =>
frm.trigger("update_clearance_date")
);
frm.change_custom_button_type(__("Get Payment Entries"), null, "default");
frm.change_custom_button_type(__("Update Clearance Date"), null, "primary");
}
}, },
}); });
}, },

View File

@@ -6,7 +6,7 @@ import frappe
from frappe import _, msgprint from frappe import _, msgprint
from frappe.model.document import Document from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, fmt_money, getdate from frappe.utils import flt, fmt_money, get_link_to_form, getdate
from pypika import Order from pypika import Order
import erpnext import erpnext
@@ -96,8 +96,11 @@ class BankClearance(Document):
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
frappe.throw( frappe.throw(
_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format( _("Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3}").format(
d.idx, d.clearance_date, d.cheque_date d.idx,
get_link_to_form(d.payment_document, d.payment_entry),
d.clearance_date,
d.cheque_date,
) )
) )
@@ -105,8 +108,18 @@ class BankClearance(Document):
if not d.clearance_date: if not d.clearance_date:
d.clearance_date = None d.clearance_date = None
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry) if d.payment_document == "Sales Invoice":
payment_entry.db_set("clearance_date", d.clearance_date) frappe.db.set_value(
"Sales Invoice Payment",
{"parent": d.payment_entry, "account": self.get("account"), "amount": [">", 0]},
"clearance_date",
d.clearance_date,
)
else:
frappe.db.set_value(
d.payment_document, d.payment_entry, "clearance_date", d.clearance_date
)
clearance_date_updated = True clearance_date_updated = True
@@ -155,7 +168,7 @@ def get_payment_entries_for_bank_clearance(
"Payment Entry" as payment_document, name as payment_entry, "Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date, reference_no as cheque_number, reference_date as cheque_date,
if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit, if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
if(paid_from=%(account)s, 0, received_amount) as debit, if(paid_from=%(account)s, 0, received_amount + total_taxes_and_charges) as debit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry` from `tabPayment Entry`

View File

@@ -1,21 +1,32 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2020, 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, getdate from frappe.utils import add_months, getdate
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.tests.utils import if_lending_app_installed, if_lending_app_not_installed from erpnext.tests.utils import if_lending_app_installed, if_lending_app_not_installed
class TestBankClearance(unittest.TestCase): class TestBankClearance(IntegrationTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
clear_payment_entries() super().setUpClass()
clear_loan_transactions() create_warehouse(
warehouse_name="_Test Warehouse",
properties={"parent_warehouse": "All Warehouses - _TC"},
company="_Test Company",
)
create_item("_Test Item")
create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company")
make_bank_account() make_bank_account()
add_transactions() add_transactions()
@@ -83,18 +94,31 @@ class TestBankClearance(unittest.TestCase):
bank_clearance.get_payment_entries() bank_clearance.get_payment_entries()
self.assertEqual(len(bank_clearance.payment_entries), 3) self.assertEqual(len(bank_clearance.payment_entries), 3)
def test_update_clearance_date_on_si(self):
sales_invoice = make_pos_sales_invoice()
def clear_payment_entries(): date = getdate()
frappe.db.delete("Payment Entry") bank_clearance = frappe.get_doc("Bank Clearance")
bank_clearance.account = "_Test Bank Clearance - _TC"
bank_clearance.from_date = add_months(date, -1)
bank_clearance.to_date = date
bank_clearance.include_pos_transactions = 1
bank_clearance.get_payment_entries()
self.assertNotEqual(len(bank_clearance.payment_entries), 0)
for payment in bank_clearance.payment_entries:
if payment.payment_entry == sales_invoice.name:
payment.clearance_date = date
@if_lending_app_installed bank_clearance.update_clearance_date()
def clear_loan_transactions():
for dt in [ si_clearance_date = frappe.db.get_value(
"Loan Disbursement", "Sales Invoice Payment",
"Loan Repayment", {"parent": sales_invoice.name, "account": bank_clearance.account},
]: "clearance_date",
frappe.db.delete(dt) )
self.assertEqual(si_clearance_date, date)
def make_bank_account(): def make_bank_account():
@@ -115,9 +139,45 @@ def add_transactions():
def make_payment_entry(): def make_payment_entry():
pi = make_purchase_invoice(supplier="_Test Supplier", qty=1, rate=690) from erpnext.buying.doctype.supplier.test_supplier import create_supplier
supplier = create_supplier(supplier_name="_Test Supplier")
pi = make_purchase_invoice(
supplier=supplier,
supplier_warehouse="_Test Warehouse - _TC",
expense_account="Cost of Goods Sold - _TC",
uom="Nos",
qty=1,
rate=690,
)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC") pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC")
pe.reference_no = "Conrad Oct 18" pe.reference_no = "Conrad Oct 18"
pe.reference_date = "2018-10-24" pe.reference_date = "2018-10-24"
pe.insert() pe.insert()
pe.submit() pe.submit()
def make_pos_sales_invoice():
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
if not frappe.db.get_value("Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}):
mode_of_payment.append(
"accounts", {"company": "_Test Company", "default_account": "_Test Bank Clearance - _TC"}
)
mode_of_payment.save()
customer = make_customer(customer="_Test Customer")
si = create_sales_invoice(customer=customer, item="_Test Item", is_pos=1, qty=1, rate=1000, do_not_save=1)
si.set("payments", [])
si.append(
"payments", {"mode_of_payment": "Cash", "account": "_Test Bank Clearance - _TC", "amount": 1000}
)
si.insert()
si.submit()
return si

View File

@@ -1,8 +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
from frappe.tests import IntegrationTestCase
class TestBankGuarantee(unittest.TestCase):
class TestBankGuarantee(IntegrationTestCase):
pass pass

View File

@@ -4,7 +4,7 @@
import frappe import frappe
from frappe import qb from frappe import qb
from frappe.tests.utils import FrappeTestCase from frappe.tests import IntegrationTestCase
from frappe.utils import add_days, today from frappe.utils import add_days, today
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
@@ -15,7 +15,7 @@ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_pay
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase): class TestBankReconciliationTool(AccountsTestMixin, IntegrationTestCase):
def setUp(self): def setUp(self):
self.create_company() self.create_company()
self.create_customer() self.create_customer()

View File

@@ -1,9 +1,10 @@
# Copyright (c) 2020, Frappe Technologies and Contributors # Copyright (c) 2020, Frappe Technologies and Contributors
# See license.txt # See license.txt
# import frappe # import frappe
import unittest import unittest
from frappe.tests import IntegrationTestCase
class TestBankStatementImport(unittest.TestCase):
class TestBankStatementImport(IntegrationTestCase):
pass pass

View File

@@ -2,13 +2,22 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests import IntegrationTestCase, UnitTestCase
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
class TestAutoMatchParty(FrappeTestCase): class UnitTestBankTransaction(UnitTestCase):
"""
Unit tests for BankTransaction.
Use this class for testing individual functions and methods.
"""
pass
class TestAutoMatchParty(IntegrationTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
create_bank_account() create_bank_account()

View File

@@ -6,7 +6,7 @@ import json
import frappe import frappe
from frappe import utils from frappe import utils
from frappe.model.docstatus import DocStatus from frappe.model.docstatus import DocStatus
from frappe.tests.utils import FrappeTestCase from frappe.tests import IntegrationTestCase, UnitTestCase
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
get_linked_payments, get_linked_payments,
@@ -18,19 +18,20 @@ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make
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.tests.utils import if_lending_app_installed from erpnext.tests.utils import if_lending_app_installed
test_dependencies = ["Item", "Cost Center"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Item", "Cost Center"]
class TestBankTransaction(FrappeTestCase): class UnitTestBankTransaction(UnitTestCase):
"""
Unit tests for BankTransaction.
Use this class for testing individual functions and methods.
"""
pass
class TestBankTransaction(IntegrationTestCase):
def setUp(self): def setUp(self):
for dt in [
"Bank Transaction",
"Payment Entry",
"Payment Entry Reference",
"POS Profile",
]:
frappe.db.delete(dt)
clear_loan_transactions()
make_pos_profile() make_pos_profile()
# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error # generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
@@ -222,11 +223,6 @@ class TestBankTransaction(FrappeTestCase):
self.assertEqual(linked_payments[0]["name"], repayment_entry.name) self.assertEqual(linked_payments[0]["name"], repayment_entry.name)
@if_lending_app_installed
def clear_loan_transactions():
frappe.db.delete("Loan Repayment")
def create_bank_account( def create_bank_account(
bank_name="Citi Bank", gl_account="_Test Bank - _TC", bank_account_name="Checking Account" bank_name="Citi Bank", gl_account="_Test Bank - _TC", bank_account_name="Checking Account"
): ):

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, 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 now_datetime, nowdate from frappe.utils import now_datetime, nowdate
from erpnext.accounts.doctype.budget.budget import BudgetError, get_actual_expense from erpnext.accounts.doctype.budget.budget import BudgetError, get_actual_expense
@@ -11,10 +11,10 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
test_dependencies = ["Monthly Distribution"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Monthly Distribution"]
class TestBudget(unittest.TestCase): class TestBudget(IntegrationTestCase):
def test_monthly_budget_crossed_ignore(self): def test_monthly_budget_crossed_ignore(self):
set_total_expense_zero(nowdate(), "cost_center") set_total_expense_zero(nowdate(), "cost_center")

View File

@@ -13,13 +13,13 @@
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Campaign", "label": "Campaign",
"options": "Campaign" "options": "UTM Campaign"
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:06:44.142625", "modified": "2024-06-28 11:04:09.815940",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Campaign Item", "name": "Campaign Item",
@@ -29,4 +29,4 @@
"sort_order": "DESC", "sort_order": "DESC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1
} }

View File

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

View File

@@ -47,9 +47,11 @@ def validate_columns(data):
no_of_columns = max([len(d) for d in data]) no_of_columns = max([len(d) for d in data])
if no_of_columns > 8: if no_of_columns != 8:
frappe.throw( frappe.throw(
_("More columns found than expected. Please compare the uploaded file with standard template"), _(
"Columns are not according to template. Please compare the uploaded file with standard template"
),
title=(_("Wrong Template")), title=(_("Wrong Template")),
) )

View File

@@ -1,8 +1,9 @@
# 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
from frappe.tests import IntegrationTestCase
class TestChartofAccountsImporter(unittest.TestCase):
class TestChartofAccountsImporter(IntegrationTestCase):
pass pass

View File

@@ -1,10 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest import unittest
# test_records = frappe.get_test_records('Cheque Print Template') from frappe.tests import IntegrationTestCase
class TestChequePrintTemplate(unittest.TestCase): class TestChequePrintTemplate(IntegrationTestCase):
pass pass

View File

@@ -1,18 +1,13 @@
# 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
test_records = frappe.get_test_records("Cost Center")
class TestCostCenter(unittest.TestCase): class TestCostCenter(IntegrationTestCase):
def test_cost_center_creation_against_child_node(self): def test_cost_center_creation_against_child_node(self):
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
frappe.get_doc(test_records[1]).insert()
cost_center = frappe.get_doc( cost_center = frappe.get_doc(
{ {
"doctype": "Cost Center", "doctype": "Cost Center",

View File

@@ -1,23 +0,0 @@
[
{
"company": "_Test Company",
"cost_center_name": "_Test Cost Center",
"doctype": "Cost Center",
"is_group": 0,
"parent_cost_center": "_Test Company - _TC"
},
{
"company": "_Test Company",
"cost_center_name": "_Test Cost Center 2",
"doctype": "Cost Center",
"is_group": 0,
"parent_cost_center": "_Test Company - _TC"
},
{
"company": "_Test Company",
"cost_center_name": "_Test Write Off Cost Center",
"doctype": "Cost Center",
"is_group": 0,
"parent_cost_center": "_Test Company - _TC"
}
]

View File

@@ -0,0 +1,18 @@
[["Cost Center"]]
company = "_Test Company"
cost_center_name = "_Test Cost Center"
is_group = 0
parent_cost_center = "_Test Company - _TC"
[["Cost Center"]]
company = "_Test Company"
cost_center_name = "_Test Cost Center 2"
is_group = 0
parent_cost_center = "_Test Company - _TC"
[["Cost Center"]]
company = "_Test Company"
cost_center_name = "_Test Write Off Cost Center"
is_group = 0
parent_cost_center = "_Test Company - _TC"

View File

@@ -1,9 +1,9 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2022, 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_days, today from frappe.utils import add_days, today
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
@@ -17,13 +17,15 @@ from erpnext.accounts.doctype.cost_center_allocation.cost_center_allocation impo
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
class TestCostCenterAllocation(unittest.TestCase): class TestCostCenterAllocation(IntegrationTestCase):
def setUp(self): def setUp(self):
cost_centers = [ cost_centers = [
"Main Cost Center 1", "Main Cost Center 1",
"Main Cost Center 2", "Main Cost Center 2",
"Main Cost Center 3",
"Sub Cost Center 1", "Sub Cost Center 1",
"Sub Cost Center 2", "Sub Cost Center 2",
"Sub Cost Center 3",
] ]
for cc in cost_centers: for cc in cost_centers:
create_cost_center(cost_center_name=cc, company="_Test Company") create_cost_center(cost_center_name=cc, company="_Test Company")
@@ -36,7 +38,7 @@ class TestCostCenterAllocation(unittest.TestCase):
) )
jv = make_journal_entry( jv = make_journal_entry(
"_Test Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True "Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True
) )
expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]] expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]]
@@ -120,7 +122,7 @@ class TestCostCenterAllocation(unittest.TestCase):
def test_valid_from_based_on_existing_gle(self): def test_valid_from_based_on_existing_gle(self):
# GLE posted against Sub Cost Center 1 on today # GLE posted against Sub Cost Center 1 on today
jv = make_journal_entry( jv = make_journal_entry(
"_Test Cash - _TC", "Cash - _TC",
"Sales - _TC", "Sales - _TC",
100, 100,
cost_center="Main Cost Center 1 - _TC", cost_center="Main Cost Center 1 - _TC",
@@ -141,6 +143,53 @@ class TestCostCenterAllocation(unittest.TestCase):
jv.cancel() jv.cancel()
def test_multiple_cost_center_allocation_on_same_main_cost_center(self):
coa1 = create_cost_center_allocation(
"_Test Company",
"Main Cost Center 3 - _TC",
{"Sub Cost Center 1 - _TC": 30, "Sub Cost Center 2 - _TC": 30, "Sub Cost Center 3 - _TC": 40},
valid_from=add_days(today(), -5),
)
coa2 = create_cost_center_allocation(
"_Test Company",
"Main Cost Center 3 - _TC",
{"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50},
valid_from=add_days(today(), -1),
)
jv = make_journal_entry(
"Cash - _TC",
"Sales - _TC",
100,
cost_center="Main Cost Center 3 - _TC",
posting_date=today(),
submit=True,
)
expected_values = {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50}
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gle)
.select(gle.cost_center, gle.debit, gle.credit)
.where(gle.voucher_type == "Journal Entry")
.where(gle.voucher_no == jv.name)
.where(gle.account == "Sales - _TC")
.orderby(gle.cost_center)
).run(as_dict=1)
self.assertTrue(gl_entries)
for gle in gl_entries:
self.assertTrue(gle.cost_center in expected_values)
self.assertEqual(gle.debit, 0)
self.assertEqual(gle.credit, expected_values[gle.cost_center])
coa1.cancel()
coa2.cancel()
jv.cancel()
def create_cost_center_allocation( def create_cost_center_allocation(
company, company,

View File

@@ -13,6 +13,7 @@
"customer", "customer",
"column_break_4", "column_break_4",
"coupon_code", "coupon_code",
"from_external_ecomm_platform",
"pricing_rule", "pricing_rule",
"uses", "uses",
"valid_from", "valid_from",
@@ -61,11 +62,12 @@
"unique": 1 "unique": 1
}, },
{ {
"depends_on": "eval: !doc.from_external_ecomm_platform",
"fieldname": "pricing_rule", "fieldname": "pricing_rule",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Pricing Rule", "label": "Pricing Rule",
"options": "Pricing Rule", "mandatory_depends_on": "eval: !doc.from_external_ecomm_platform",
"reqd": 1 "options": "Pricing Rule"
}, },
{ {
"fieldname": "uses", "fieldname": "uses",
@@ -114,13 +116,20 @@
"options": "Coupon Code", "options": "Coupon Code",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fieldname": "from_external_ecomm_platform",
"fieldtype": "Check",
"label": "From External Ecomm Platform"
} }
], ],
"links": [], "links": [],
"modified": "2024-03-27 13:06:47.220931", "modified": "2024-11-19 16:35:11.836441",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Coupon Code", "name": "Coupon Code",
"naming_rule": "By fieldname",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@@ -23,8 +23,9 @@ class CouponCode(Document):
coupon_type: DF.Literal["Promotional", "Gift Card"] coupon_type: DF.Literal["Promotional", "Gift Card"]
customer: DF.Link | None customer: DF.Link | None
description: DF.TextEditor | None description: DF.TextEditor | None
from_external_ecomm_platform: DF.Check
maximum_use: DF.Int maximum_use: DF.Int
pricing_rule: DF.Link pricing_rule: DF.Link | None
used: DF.Int used: DF.Int
valid_from: DF.Date | None valid_from: DF.Date | None
valid_upto: DF.Date | None valid_upto: DF.Date | None

View File

@@ -1,13 +1,13 @@
# 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 erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
test_dependencies = ["Item"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Item"]
def test_create_test_data(): def test_create_test_data():
@@ -110,7 +110,7 @@ def test_create_test_data():
coupon_code.insert() coupon_code.insert()
class TestCouponCode(unittest.TestCase): class TestCouponCode(IntegrationTestCase):
def setUp(self): def setUp(self):
test_create_test_data() test_create_test_data()
@@ -142,3 +142,39 @@ class TestCouponCode(unittest.TestCase):
so.submit() so.submit()
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1) self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
def test_coupon_without_max_use(self):
from erpnext.accounts.doctype.pricing_rule.utils import (
update_coupon_code_count,
validate_coupon_code,
)
coupon = frappe.get_doc(
{
"doctype": "Coupon Code",
"coupon_name": "_Test Coupon Without Max Use",
"coupon_code": "TESTUNLIMITED",
"from_external_ecomm_platform": 1, # avoids requirement for pricing rule
"valid_from": frappe.utils.nowdate(),
"maximum_use": 0,
"used": 0,
}
)
coupon.insert(ignore_permissions=True)
# Validate initial state
self.assertEqual(coupon.used, 0)
self.assertEqual(coupon.maximum_use, 0)
# Use coupon multiple times
for _ in range(5):
validate_coupon_code(coupon.name)
update_coupon_code_count(coupon.name, "used")
coupon.reload()
# Check that the coupon is still valid and usage count increased
self.assertEqual(coupon.used, 5)
validate_coupon_code(coupon.name) # This should not raise an error
# Clean up
coupon.delete()

View File

@@ -109,7 +109,7 @@ def get_api_endpoint(service_provider: str | None = None, use_http: bool = False
if service_provider == "exchangerate.host": if service_provider == "exchangerate.host":
api = "api.exchangerate.host/convert" api = "api.exchangerate.host/convert"
elif service_provider == "frankfurter.app": elif service_provider == "frankfurter.app":
api = "frankfurter.app/{transaction_date}" api = "api.frankfurter.app/{transaction_date}"
protocol = "https://" protocol = "https://"
if use_http: if use_http:

View File

@@ -1,9 +1,10 @@
# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors # Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt # For license information, please see license.txt
# import frappe # import frappe
import unittest import unittest
from frappe.tests import IntegrationTestCase
class TestCurrencyExchangeSettings(unittest.TestCase):
class TestCurrencyExchangeSettings(IntegrationTestCase):
pass pass

View File

@@ -19,16 +19,6 @@
"currency", "currency",
"column_break_11", "column_break_11",
"conversion_rate", "conversion_rate",
"address_and_contact_section",
"customer_address",
"address_display",
"contact_person",
"contact_display",
"column_break_16",
"company_address",
"company_address_display",
"contact_mobile",
"contact_email",
"section_break_6", "section_break_6",
"dunning_type", "dunning_type",
"column_break_8", "column_break_8",
@@ -56,7 +46,21 @@
"income_account", "income_account",
"column_break_48", "column_break_48",
"cost_center", "cost_center",
"amended_from" "amended_from",
"address_and_contact_tab",
"address_and_contact_section",
"customer_address",
"address_display",
"column_break_vodj",
"contact_person",
"contact_display",
"contact_mobile",
"contact_email",
"section_break_xban",
"column_break_16",
"company_address",
"company_address_display",
"column_break_lqmf"
], ],
"fields": [ "fields": [
{ {
@@ -178,10 +182,8 @@
"label": "Rate of Interest (%) Yearly" "label": "Rate of Interest (%) Yearly"
}, },
{ {
"collapsible": 1,
"fieldname": "address_and_contact_section", "fieldname": "address_and_contact_section",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"label": "Address and Contact"
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
@@ -377,11 +379,28 @@
{ {
"fieldname": "column_break_48", "fieldname": "column_break_48",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"fieldname": "address_and_contact_tab",
"fieldtype": "Tab Break",
"label": "Address & Contact"
},
{
"fieldname": "column_break_vodj",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_xban",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_lqmf",
"fieldtype": "Column Break"
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:08:19.176146", "modified": "2024-11-26 13:46:07.760867",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Dunning", "name": "Dunning",

View File

@@ -220,19 +220,31 @@ def get_linked_dunnings_as_per_state(sales_invoice, state):
@frappe.whitelist() @frappe.whitelist()
def get_dunning_letter_text(dunning_type, doc, language=None): def get_dunning_letter_text(dunning_type: str, doc: str | dict, language: str | None = None) -> dict:
DOCTYPE = "Dunning Letter Text"
FIELDS = ["body_text", "closing_text", "language"]
if isinstance(doc, str): if isinstance(doc, str):
doc = json.loads(doc) doc = json.loads(doc)
if not language:
language = doc.get("language")
if language: if language:
filters = {"parent": dunning_type, "language": language} letter_text = frappe.db.get_value(
else: DOCTYPE, {"parent": dunning_type, "language": language}, FIELDS, as_dict=1
filters = {"parent": dunning_type, "is_default_language": 1} )
letter_text = frappe.db.get_value(
"Dunning Letter Text", filters, ["body_text", "closing_text", "language"], as_dict=1 if not letter_text:
) letter_text = frappe.db.get_value(
if letter_text: DOCTYPE, {"parent": dunning_type, "is_default_language": 1}, FIELDS, as_dict=1
return { )
"body_text": frappe.render_template(letter_text.body_text, doc),
"closing_text": frappe.render_template(letter_text.closing_text, doc), if not letter_text:
"language": letter_text.language, return {}
}
return {
"body_text": frappe.render_template(letter_text.body_text, doc),
"closing_text": frappe.render_template(letter_text.closing_text, doc),
"language": letter_text.language,
}

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import frappe import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests import IntegrationTestCase, UnitTestCase
from frappe.utils import add_days, nowdate, today from frappe.utils import add_days, nowdate, today
from erpnext import get_default_cost_center from erpnext import get_default_cost_center
@@ -16,10 +16,19 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
create_sales_invoice_against_cost_center, create_sales_invoice_against_cost_center,
) )
test_dependencies = ["Company", "Cost Center"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Company", "Cost Center"]
class TestDunning(FrappeTestCase): class UnitTestDunning(UnitTestCase):
"""
Unit tests for Dunning.
Use this class for testing individual functions and methods.
"""
pass
class TestDunning(IntegrationTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()

View File

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

View File

@@ -1,36 +0,0 @@
[
{
"doctype": "Dunning Type",
"dunning_type": "_Test First Notice",
"company": "_Test Company",
"is_default": 1,
"dunning_fee": 0.0,
"rate_of_interest": 0.0,
"dunning_letter_text": [
{
"language": "en",
"body_text": "We have still not received payment for our invoice",
"closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees."
}
],
"income_account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC"
},
{
"doctype": "Dunning Type",
"dunning_type": "_Test Second Notice",
"company": "_Test Company",
"is_default": 0,
"dunning_fee": 10.0,
"rate_of_interest": 10.0,
"dunning_letter_text": [
{
"language": "en",
"body_text": "We have still not received payment for our invoice",
"closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees."
}
],
"income_account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC"
}
]

View File

@@ -0,0 +1,28 @@
[["Dunning Type"]]
dunning_type = "_Test First Notice"
company = "_Test Company"
is_default = 1
dunning_fee = 0.0
rate_of_interest = 0.0
income_account = "Sales - _TC"
cost_center = "_Test Cost Center - _TC"
[["Dunning Type".dunning_letter_text]]
language = "en"
body_text = "We have still not received payment for our invoice"
closing_text = "We kindly request that you pay the outstanding amount immediately, including interest and late fees."
[["Dunning Type"]]
dunning_type = "_Test Second Notice"
company = "_Test Company"
is_default = 0
dunning_fee = 10.0
rate_of_interest = 10.0
income_account = "Sales - _TC"
cost_center = "_Test Cost Center - _TC"
[["Dunning Type".dunning_letter_text]]
language = "en"
body_text = "We have still not received payment for our invoice"
closing_text = "We kindly request that you pay the outstanding amount immediately, including interest and late fees."

View File

@@ -85,18 +85,16 @@ frappe.ui.form.on("Exchange Rate Revaluation", {
}, },
make_jv: function (frm) { make_jv: function (frm) {
let revaluation_journal = null;
let zero_balance_journal = null;
frappe.call({ frappe.call({
method: "make_jv_entries", method: "make_jv_entries",
doc: frm.doc, doc: frm.doc,
freeze: true, freeze: true,
freeze_message: "Making Journal Entries...", freeze_message: __("Creating Journal Entries..."),
callback: function (r) { callback: function (r) {
if (r.message) { if (r.message) {
let response = r.message; let response = r.message;
if (response["revaluation_jv"] || response["zero_balance_jv"]) { if (response["revaluation_jv"] || response["zero_balance_jv"]) {
frappe.msgprint(__("Journals have been created")); frappe.msgprint(__("Journal entries have been created"));
} }
} }
}, },

View File

@@ -74,6 +74,21 @@ class ExchangeRateRevaluation(Document):
if not (self.company and self.posting_date): if not (self.company and self.posting_date):
frappe.throw(_("Please select Company and Posting Date to getting entries")) frappe.throw(_("Please select Company and Posting Date to getting entries"))
def before_submit(self):
self.remove_accounts_without_gain_loss()
def remove_accounts_without_gain_loss(self):
self.accounts = [account for account in self.accounts if account.gain_loss]
if not self.accounts:
frappe.throw(_("At least one account with exchange gain or loss is required"))
frappe.msgprint(
_("Removing rows without exchange gain or loss"),
alert=True,
indicator="yellow",
)
def on_cancel(self): def on_cancel(self):
self.ignore_linked_doctypes = "GL Entry" self.ignore_linked_doctypes = "GL Entry"
@@ -248,23 +263,23 @@ class ExchangeRateRevaluation(Document):
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date) new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date)
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate) new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision) gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
if gain_loss:
accounts.append( accounts.append(
{ {
"account": d.account, "account": d.account,
"party_type": d.party_type, "party_type": d.party_type,
"party": d.party, "party": d.party,
"account_currency": d.account_currency, "account_currency": d.account_currency,
"balance_in_base_currency": d.balance, "balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency, "balance_in_account_currency": d.balance_in_account_currency,
"zero_balance": d.zero_balance, "zero_balance": d.zero_balance,
"current_exchange_rate": current_exchange_rate, "current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate, "new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency, "new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_account_currency": d.balance_in_account_currency, "new_balance_in_account_currency": d.balance_in_account_currency,
"gain_loss": gain_loss, "gain_loss": gain_loss,
} }
) )
# Handle Accounts with '0' balance in Account/Base Currency # Handle Accounts with '0' balance in Account/Base Currency
for d in [x for x in account_details if x.zero_balance]: for d in [x for x in account_details if x.zero_balance]:
@@ -288,23 +303,22 @@ class ExchangeRateRevaluation(Document):
current_exchange_rate * d.balance_in_account_currency current_exchange_rate * d.balance_in_account_currency
) )
if gain_loss: accounts.append(
accounts.append( {
{ "account": d.account,
"account": d.account, "party_type": d.party_type,
"party_type": d.party_type, "party": d.party,
"party": d.party, "account_currency": d.account_currency,
"account_currency": d.account_currency, "balance_in_base_currency": d.balance,
"balance_in_base_currency": d.balance, "balance_in_account_currency": d.balance_in_account_currency,
"balance_in_account_currency": d.balance_in_account_currency, "zero_balance": d.zero_balance,
"zero_balance": d.zero_balance, "current_exchange_rate": current_exchange_rate,
"current_exchange_rate": current_exchange_rate, "new_exchange_rate": new_exchange_rate,
"new_exchange_rate": new_exchange_rate, "new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_base_currency": new_balance_in_base_currency, "new_balance_in_account_currency": new_balance_in_account_currency,
"new_balance_in_account_currency": new_balance_in_account_currency, "gain_loss": gain_loss,
"gain_loss": gain_loss, }
} )
)
return accounts return accounts

View File

@@ -3,7 +3,7 @@
import frappe import frappe
from frappe.tests.utils import FrappeTestCase, change_settings from frappe.tests import IntegrationTestCase
from frappe.utils import add_days, flt, today from frappe.utils import add_days, flt, today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -11,7 +11,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase): class TestExchangeRateRevaluation(AccountsTestMixin, IntegrationTestCase):
def setUp(self): def setUp(self):
self.create_company() self.create_company()
self.create_usd_receivable_account() self.create_usd_receivable_account()
@@ -35,7 +35,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
company_doc.unrealized_exchange_gain_loss_account = company_doc.exchange_gain_loss_account company_doc.unrealized_exchange_gain_loss_account = company_doc.exchange_gain_loss_account
company_doc.save() company_doc.save()
@change_settings( @IntegrationTestCase.change_settings(
"Accounts Settings", "Accounts Settings",
{"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0}, {"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0},
) )
@@ -88,7 +88,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
)[0] )[0]
self.assertEqual(acc_balance.balance, 8500.0) self.assertEqual(acc_balance.balance, 8500.0)
@change_settings( @IntegrationTestCase.change_settings(
"Accounts Settings", "Accounts Settings",
{"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0}, {"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0},
) )
@@ -158,7 +158,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
self.assertEqual(acc_balance.balance, 0.0) self.assertEqual(acc_balance.balance, 0.0)
self.assertEqual(acc_balance.balance_in_account_currency, 0.0) self.assertEqual(acc_balance.balance_in_account_currency, 0.0)
@change_settings( @IntegrationTestCase.change_settings(
"Accounts Settings", "Accounts Settings",
{"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0}, {"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0},
) )
@@ -188,7 +188,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
pe = get_payment_entry(si.doctype, si.name) pe = get_payment_entry(si.doctype, si.name)
pe.paid_amount = 95 pe.paid_amount = 95
pe.source_exchange_rate = 84.211 pe.source_exchange_rate = 84.2105
pe.received_amount = 8000 pe.received_amount = 8000
pe.references = [] pe.references = []
pe.save().submit() pe.save().submit()
@@ -229,7 +229,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
row = next(x for x in je.accounts if x.account == self.debtors_usd) row = next(x for x in je.accounts if x.account == self.debtors_usd)
self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD
row = next(x for x in je.accounts if x.account != self.debtors_usd) row = next(x for x in je.accounts if x.account != self.debtors_usd)
self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR self.assertEqual(flt(row.debit_in_account_currency, precision), 421.05) # in INR
# total_debit and total_credit will be 0.0, as JV is posting only to account currency fields # total_debit and total_credit will be 0.0, as JV is posting only to account currency fields
self.assertEqual(flt(je.total_debit, precision), 0.0) self.assertEqual(flt(je.total_debit, precision), 0.0)
@@ -247,7 +247,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
self.assertEqual(flt(acc_balance.balance, precision), 0.0) self.assertEqual(flt(acc_balance.balance, precision), 0.0)
self.assertEqual(flt(acc_balance.balance_in_account_currency, precision), 0.0) self.assertEqual(flt(acc_balance.balance_in_account_currency, precision), 0.0)
@change_settings( @IntegrationTestCase.change_settings(
"Accounts Settings", "Accounts Settings",
{"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0}, {"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0},
) )

View File

@@ -1,14 +1,14 @@
# 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 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
class TestFinanceBook(unittest.TestCase): class TestFinanceBook(IntegrationTestCase):
def test_finance_book(self): def test_finance_book(self):
finance_book = create_finance_book() finance_book = create_finance_book()

View File

@@ -4,10 +4,7 @@
frappe.ui.form.on("Fiscal Year", { frappe.ui.form.on("Fiscal Year", {
onload: function (frm) { onload: function (frm) {
if (frm.doc.__islocal) { if (frm.doc.__islocal) {
frm.set_value( frm.set_value("year_start_date", frappe.datetime.year_start());
"year_start_date",
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)
);
} }
}, },
year_start_date: function (frm) { year_start_date: function (frm) {

View File

@@ -72,10 +72,10 @@
}, },
{ {
"default": "0", "default": "0",
"description": "Less than 12 months.", "description": "More/Less than 12 months.",
"fieldname": "is_short_year", "fieldname": "is_short_year",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Short Year", "label": "Is Short/Long Year",
"set_only_once": 1 "set_only_once": 1
} }
], ],

View File

@@ -1,16 +1,15 @@
# 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.utils import now_datetime from frappe.utils import now_datetime
test_ignore = ["Company"] IGNORE_TEST_RECORD_DEPENDENCIES = ["Company"]
class TestFiscalYear(unittest.TestCase): class TestFiscalYear(IntegrationTestCase):
def test_extra_year(self): def test_extra_year(self):
if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"): if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"):
frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000") frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000")
@@ -39,8 +38,21 @@ def test_record_generator():
] ]
start = 2012 start = 2012
this_year = now_datetime().year
end = now_datetime().year + 25 end = now_datetime().year + 25
for year in range(start, end): # The current year fails to load with the following error:
# Year start date or end date is overlapping with 2024. To avoid please set company
# This is a quick-fix: if current FY is needed, please refactor test data properly
for year in range(start, this_year):
test_records.append(
{
"doctype": "Fiscal Year",
"year": f"_Test Fiscal Year {year}",
"year_start_date": f"{year}-01-01",
"year_end_date": f"{year}-12-31",
}
)
for year in range(this_year + 1, end):
test_records.append( test_records.append(
{ {
"doctype": "Fiscal Year", "doctype": "Fiscal Year",

View File

@@ -6,38 +6,50 @@
"document_type": "Document", "document_type": "Document",
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"dates_section",
"posting_date", "posting_date",
"transaction_date", "transaction_date",
"column_break_avko",
"fiscal_year",
"due_date",
"account_details_section",
"account", "account",
"account_currency",
"column_break_ifvf",
"against",
"party_type", "party_type",
"party", "party",
"cost_center", "transaction_details_section",
"debit", "voucher_type",
"credit", "voucher_no",
"account_currency", "voucher_subtype",
"debit_in_account_currency", "transaction_currency",
"credit_in_account_currency", "column_break_dpsx",
"against",
"against_voucher_type", "against_voucher_type",
"against_voucher", "against_voucher",
"voucher_type",
"voucher_subtype",
"voucher_no",
"voucher_detail_no", "voucher_detail_no",
"transaction_exchange_rate",
"amounts_section",
"debit_in_account_currency",
"debit",
"debit_in_transaction_currency",
"column_break_bm1w",
"credit_in_account_currency",
"credit",
"credit_in_transaction_currency",
"dimensions_section",
"cost_center",
"column_break_lmnm",
"project", "project",
"remarks", "more_info_section",
"finance_book",
"company",
"is_opening", "is_opening",
"is_advance", "is_advance",
"fiscal_year", "column_break_8abq",
"company",
"finance_book",
"to_rename", "to_rename",
"due_date",
"is_cancelled", "is_cancelled",
"transaction_currency", "remarks"
"debit_in_transaction_currency",
"credit_in_transaction_currency",
"transaction_exchange_rate"
], ],
"fields": [ "fields": [
{ {
@@ -285,13 +297,67 @@
"fieldname": "voucher_subtype", "fieldname": "voucher_subtype",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"label": "Voucher Subtype" "label": "Voucher Subtype"
},
{
"fieldname": "dates_section",
"fieldtype": "Section Break",
"label": "Dates"
},
{
"fieldname": "column_break_avko",
"fieldtype": "Column Break"
},
{
"fieldname": "account_details_section",
"fieldtype": "Section Break",
"label": "Account Details"
},
{
"fieldname": "column_break_ifvf",
"fieldtype": "Column Break"
},
{
"fieldname": "transaction_details_section",
"fieldtype": "Section Break",
"label": "Transaction Details"
},
{
"fieldname": "amounts_section",
"fieldtype": "Section Break",
"label": "Amounts"
},
{
"fieldname": "column_break_dpsx",
"fieldtype": "Column Break"
},
{
"fieldname": "more_info_section",
"fieldtype": "Section Break",
"label": "More Info"
},
{
"fieldname": "column_break_bm1w",
"fieldtype": "Column Break"
},
{
"fieldname": "dimensions_section",
"fieldtype": "Section Break",
"label": "Dimensions"
},
{
"fieldname": "column_break_lmnm",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_8abq",
"fieldtype": "Column Break"
} }
], ],
"icon": "fa fa-list", "icon": "fa fa-list",
"idx": 1, "idx": 1,
"in_create": 1, "in_create": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:09:45.205364", "modified": "2024-08-22 13:03:39.997475",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "GL Entry", "name": "GL Entry",

View File

@@ -430,8 +430,9 @@ def update_against_account(voucher_type, voucher_no):
def on_doctype_update(): def on_doctype_update():
frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"]) frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
frappe.db.add_index("GL Entry", ["posting_date", "company"])
frappe.db.add_index("GL Entry", ["party_type", "party"])
def rename_gle_sle_docs(): def rename_gle_sle_docs():

View File

@@ -1,17 +1,16 @@
# 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.model.naming import parse_naming_series from frappe.model.naming import parse_naming_series
from frappe.tests import IntegrationTestCase
from erpnext.accounts.doctype.gl_entry.gl_entry import rename_gle_sle_docs from erpnext.accounts.doctype.gl_entry.gl_entry import rename_gle_sle_docs
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
class TestGLEntry(unittest.TestCase): class TestGLEntry(IntegrationTestCase):
def test_round_off_entry(self): def test_round_off_entry(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "_Test Write Off - _TC") frappe.db.set_value("Company", "_Test Company", "round_off_account", "_Test Write Off - _TC")
frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC") frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")

View File

@@ -1,9 +1,9 @@
# 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 frappe.utils import add_days, flt, nowdate from frappe.utils import add_days, flt, nowdate
from erpnext.accounts.doctype.account.test_account import create_account from erpnext.accounts.doctype.account.test_account import create_account
@@ -12,7 +12,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
class TestInvoiceDiscounting(unittest.TestCase): class TestInvoiceDiscounting(IntegrationTestCase):
def setUp(self): def setUp(self):
self.ar_credit = create_account( self.ar_credit = create_account(
account_name="_Test Accounts Receivable Credit", account_name="_Test Accounts Receivable Credit",

View File

@@ -1,8 +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
from frappe.tests import IntegrationTestCase
class TestItemTaxTemplate(unittest.TestCase):
class TestItemTaxTemplate(IntegrationTestCase):
pass pass

View File

@@ -1,79 +0,0 @@
[
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 10",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
"parentfield": "taxes",
"tax_rate": 10,
"tax_type": "_Test Account Excise Duty - _TC"
}
]
},
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 12",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
"parentfield": "taxes",
"tax_rate": 12,
"tax_type": "_Test Account Excise Duty - _TC"
}
]
},
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 15",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
"parentfield": "taxes",
"tax_rate": 15,
"tax_type": "_Test Account Excise Duty - _TC"
}
]
},
{
"doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 20",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
"parentfield": "taxes",
"tax_rate": 20,
"tax_type": "_Test Account Excise Duty - _TC"
}
]
},
{
"doctype": "Item Tax Template",
"title": "_Test Item Tax Template 1",
"company": "_Test Company",
"taxes": [
{
"doctype": "Item Tax Template Detail",
"parentfield": "taxes",
"tax_rate": 5,
"tax_type": "_Test Account Excise Duty - _TC"
},
{
"doctype": "Item Tax Template Detail",
"parentfield": "taxes",
"tax_rate": 10,
"tax_type": "_Test Account Education Cess - _TC"
},
{
"doctype": "Item Tax Template Detail",
"parentfield": "taxes",
"tax_rate": 15,
"tax_type": "_Test Account S&H Education Cess - _TC"
}
]
}
]

View File

@@ -0,0 +1,62 @@
[["Item Tax Template"]]
title = "_Test Account Excise Duty @ 10"
company = "_Test Company"
[["Item Tax Template".taxes]]
doctype = "Item Tax Template Detail"
parentfield = "taxes"
tax_rate = 10
tax_type = "_Test Account Excise Duty - _TC"
[["Item Tax Template"]]
title = "_Test Account Excise Duty @ 12"
company = "_Test Company"
[["Item Tax Template".taxes]]
doctype = "Item Tax Template Detail"
parentfield = "taxes"
tax_rate = 12
tax_type = "_Test Account Excise Duty - _TC"
[["Item Tax Template"]]
title = "_Test Account Excise Duty @ 15"
company = "_Test Company"
[["Item Tax Template".taxes]]
doctype = "Item Tax Template Detail"
parentfield = "taxes"
tax_rate = 15
tax_type = "_Test Account Excise Duty - _TC"
[["Item Tax Template"]]
title = "_Test Account Excise Duty @ 20"
company = "_Test Company"
[["Item Tax Template".taxes]]
doctype = "Item Tax Template Detail"
parentfield = "taxes"
tax_rate = 20
tax_type = "_Test Account Excise Duty - _TC"
[["Item Tax Template"]]
title = "_Test Item Tax Template 1"
company = "_Test Company"
[["Item Tax Template".taxes]]
doctype = "Item Tax Template Detail"
parentfield = "taxes"
tax_rate = 5
tax_type = "_Test Account Excise Duty - _TC"
[["Item Tax Template".taxes]]
doctype = "Item Tax Template Detail"
parentfield = "taxes"
tax_rate = 10
tax_type = "_Test Account Education Cess - _TC"
[["Item Tax Template".taxes]]
doctype = "Item Tax Template Detail"
parentfield = "taxes"
tax_rate = 15
tax_type = "_Test Account S&H Education Cess - _TC"

View File

@@ -25,30 +25,6 @@ frappe.ui.form.on("Journal Entry", {
refresh: function (frm) { refresh: function (frm) {
erpnext.toggle_naming_series(); erpnext.toggle_naming_series();
if (frm.doc.repost_required && frm.doc.docstatus === 1) {
frm.set_intro(
__(
"Accounting entries for this Journal Entry need to be reposted. Please click on 'Repost' button to update."
)
);
frm.add_custom_button(__("Repost Accounting Entries"), () => {
frm.call({
doc: frm.doc,
method: "repost_accounting_entries",
freeze: true,
freeze_message: __("Reposting..."),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__("Accounting Entries are reposted."));
frm.refresh();
}
},
});
})
.removeClass("btn-default")
.addClass("btn-warning");
}
if (frm.doc.docstatus > 0) { if (frm.doc.docstatus > 0) {
frm.add_custom_button( frm.add_custom_button(
__("Ledger"), __("Ledger"),
@@ -384,21 +360,23 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
accounts_add(doc, cdt, cdn) { accounts_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn); var row = frappe.get_doc(cdt, cdn);
row.exchange_rate = 1;
$.each(doc.accounts, function (i, d) { $.each(doc.accounts, function (i, d) {
if (d.account && d.party && d.party_type) { if (d.account && d.party && d.party_type) {
row.account = d.account; row.account = d.account;
row.party = d.party; row.party = d.party;
row.party_type = d.party_type; row.party_type = d.party_type;
row.exchange_rate = d.exchange_rate;
} }
}); });
// set difference // set difference
if (doc.difference) { if (doc.difference) {
if (doc.difference > 0) { if (doc.difference > 0) {
row.credit_in_account_currency = doc.difference; row.credit_in_account_currency = doc.difference / row.exchange_rate;
row.credit = doc.difference; row.credit = doc.difference;
} else { } else {
row.debit_in_account_currency = -doc.difference; row.debit_in_account_currency = -doc.difference / row.exchange_rate;
row.debit = -doc.difference; row.debit = -doc.difference;
} }
} }
@@ -704,6 +682,7 @@ $.extend(erpnext.journal_entry, {
callback: function (r) { callback: function (r) {
if (r.message) { if (r.message) {
$.extend(d, r.message); $.extend(d, r.message);
erpnext.journal_entry.set_amount_on_last_row(frm, dt, dn);
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, dt, dn); erpnext.journal_entry.set_debit_credit_in_company_currency(frm, dt, dn);
refresh_field("accounts"); refresh_field("accounts");
} }
@@ -711,4 +690,26 @@ $.extend(erpnext.journal_entry, {
}); });
} }
}, },
set_amount_on_last_row: function (frm, dt, dn) {
let row = locals[dt][dn];
let length = frm.doc.accounts.length;
if (row.idx != length) return;
let difference = frm.doc.accounts.reduce((total, row) => {
if (row.idx == length) return total;
return total + row.debit - row.credit;
}, 0);
if (difference) {
if (difference > 0) {
row.credit_in_account_currency = difference / row.exchange_rate;
row.credit = difference;
} else {
row.debit_in_account_currency = -difference / row.exchange_rate;
row.debit = -difference;
}
}
refresh_field("accounts");
},
}); });

View File

@@ -64,8 +64,7 @@
"stock_entry", "stock_entry",
"subscription_section", "subscription_section",
"auto_repeat", "auto_repeat",
"amended_from", "amended_from"
"repost_required"
], ],
"fields": [ "fields": [
{ {
@@ -544,15 +543,6 @@
"label": "Is System Generated", "label": "Is System Generated",
"no_copy": 1, "no_copy": 1,
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"print_hide": 1,
"read_only": 1
} }
], ],
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
@@ -567,7 +557,7 @@
"table_fieldname": "payment_entries" "table_fieldname": "payment_entries"
} }
], ],
"modified": "2024-03-27 13:09:58.366953", "modified": "2024-07-18 15:32:29.413598",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@@ -47,9 +47,7 @@ class JournalEntry(AccountsController):
if TYPE_CHECKING: if TYPE_CHECKING:
from frappe.types import DF from frappe.types import DF
from erpnext.accounts.doctype.journal_entry_account.journal_entry_account import ( from erpnext.accounts.doctype.journal_entry_account.journal_entry_account import JournalEntryAccount
JournalEntryAccount,
)
accounts: DF.Table[JournalEntryAccount] accounts: DF.Table[JournalEntryAccount]
amended_from: DF.Link | None amended_from: DF.Link | None
@@ -129,9 +127,6 @@ class JournalEntry(AccountsController):
self.set_amounts_in_company_currency() self.set_amounts_in_company_currency()
self.validate_debit_credit_amount() self.validate_debit_credit_amount()
self.set_total_debit_credit() self.set_total_debit_credit()
# Do not validate while importing via data import
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
if not frappe.flags.is_reverse_depr_entry: if not frappe.flags.is_reverse_depr_entry:
self.validate_against_jv() self.validate_against_jv()
@@ -186,10 +181,16 @@ class JournalEntry(AccountsController):
else: else:
return self._cancel() return self._cancel()
def before_submit(self):
# Do not validate while importing via data import
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
def on_submit(self): def on_submit(self):
self.validate_cheque_info() self.validate_cheque_info()
self.check_credit_limit() self.check_credit_limit()
self.make_gl_entries() self.make_gl_entries()
self.make_advance_payment_ledger_entries()
self.update_advance_paid() self.update_advance_paid()
self.update_asset_value() self.update_asset_value()
self.update_inter_company_jv() self.update_inter_company_jv()
@@ -197,14 +198,15 @@ class JournalEntry(AccountsController):
self.update_booked_depreciation() self.update_booked_depreciation()
def on_update_after_submit(self): def on_update_after_submit(self):
if hasattr(self, "repost_required"): # Flag will be set on Reconciliation
self.needs_repost = self.check_if_fields_updated( # Reconciliation tool will anyways repost ledger entries. So, no need to check and do implicit repost.
fields_to_check=[], child_tables={"accounts": []} if self.flags.get("ignore_reposting_on_reconciliation"):
) return
if self.needs_repost:
self.validate_for_repost() self.needs_repost = self.check_if_fields_updated(fields_to_check=[], child_tables={"accounts": []})
self.db_set("repost_required", self.needs_repost) if self.needs_repost:
self.repost_accounting_entries() self.validate_for_repost()
self.repost_accounting_entries()
def on_cancel(self): def on_cancel(self):
# References for this Journal are removed on the `on_cancel` event in accounts_controller # References for this Journal are removed on the `on_cancel` event in accounts_controller
@@ -219,8 +221,10 @@ class JournalEntry(AccountsController):
"Repost Accounting Ledger Items", "Repost Accounting Ledger Items",
"Unreconcile Payment", "Unreconcile Payment",
"Unreconcile Payment Entries", "Unreconcile Payment Entries",
"Advance Payment Ledger Entry",
) )
self.make_gl_entries(1) self.make_gl_entries(1)
self.make_advance_payment_ledger_entries()
self.update_advance_paid() self.update_advance_paid()
self.unlink_advance_entry_reference() self.unlink_advance_entry_reference()
self.unlink_asset_reference() self.unlink_asset_reference()
@@ -263,7 +267,7 @@ class JournalEntry(AccountsController):
frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation")) frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
def validate_stock_accounts(self): def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) stock_accounts = get_stock_accounts(self.company, accounts=self.accounts)
for account in stock_accounts: for account in stock_accounts:
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance( account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
account, self.posting_date, self.company account, self.posting_date, self.company
@@ -1674,6 +1678,8 @@ def make_reverse_journal_entry(source_name, target_doc=None):
"debit": "credit", "debit": "credit",
"credit_in_account_currency": "debit_in_account_currency", "credit_in_account_currency": "debit_in_account_currency",
"credit": "debit", "credit": "debit",
"reference_type": "reference_type",
"reference_name": "reference_name",
}, },
}, },
}, },

View File

@@ -1,11 +1,8 @@
# 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 frappe import frappe
from frappe.tests.utils import change_settings from frappe.tests import IntegrationTestCase, UnitTestCase
from frappe.utils import flt, nowdate from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
@@ -13,25 +10,36 @@ from erpnext.accounts.doctype.journal_entry.journal_entry import StockAccountInv
from erpnext.exceptions import InvalidAccountCurrency from erpnext.exceptions import InvalidAccountCurrency
class TestJournalEntry(unittest.TestCase): class UnitTestJournalEntry(UnitTestCase):
@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1}) """
Unit tests for JournalEntry.
Use this class for testing individual functions and methods.
"""
pass
class TestJournalEntry(IntegrationTestCase):
@IntegrationTestCase.change_settings(
"Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1}
)
def test_journal_entry_with_against_jv(self): def test_journal_entry_with_against_jv(self):
jv_invoice = frappe.copy_doc(test_records[2]) jv_invoice = frappe.copy_doc(self.globalTestRecords["Journal Entry"][2])
base_jv = frappe.copy_doc(test_records[0]) base_jv = frappe.copy_doc(self.globalTestRecords["Journal Entry"][0])
self.jv_against_voucher_testcase(base_jv, jv_invoice) self.jv_against_voucher_testcase(base_jv, jv_invoice)
def test_jv_against_sales_order(self): def test_jv_against_sales_order(self):
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
sales_order = make_sales_order(do_not_save=True) sales_order = make_sales_order(do_not_save=True)
base_jv = frappe.copy_doc(test_records[0]) base_jv = frappe.copy_doc(self.globalTestRecords["Journal Entry"][0])
self.jv_against_voucher_testcase(base_jv, sales_order) self.jv_against_voucher_testcase(base_jv, sales_order)
def test_jv_against_purchase_order(self): def test_jv_against_purchase_order(self):
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
purchase_order = create_purchase_order(do_not_save=True) purchase_order = create_purchase_order(do_not_save=True)
base_jv = frappe.copy_doc(test_records[1]) base_jv = frappe.copy_doc(self.globalTestRecords["Journal Entry"][1])
self.jv_against_voucher_testcase(base_jv, purchase_order) self.jv_against_voucher_testcase(base_jv, purchase_order)
def jv_against_voucher_testcase(self, base_jv, test_voucher): def jv_against_voucher_testcase(self, base_jv, test_voucher):
@@ -515,6 +523,72 @@ class TestJournalEntry(unittest.TestCase):
self.assertEqual(row.debit_in_account_currency, 100) self.assertEqual(row.debit_in_account_currency, 100)
self.assertEqual(row.credit_in_account_currency, 100) self.assertEqual(row.credit_in_account_currency, 100)
def test_toggle_debit_credit_if_negative(self):
from erpnext.accounts.general_ledger import process_gl_map
# Create JV with defaut cost center - _Test Cost Center
frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
jv = frappe.new_doc("Journal Entry")
jv.posting_date = nowdate()
jv.company = "_Test Company"
jv.user_remark = "test"
jv.extend(
"accounts",
[
{
"account": "_Test Cash - _TC",
"debit": 100 * -1,
"debit_in_account_currency": 100 * -1,
"exchange_rate": 1,
},
{
"account": "_Test Bank - _TC",
"credit": 100 * -1,
"credit_in_account_currency": 100 * -1,
"exchange_rate": 1,
},
],
)
jv.flags.ignore_validate = True
jv.save()
self.assertEqual(len(jv.accounts), 2)
gl_map = jv.build_gl_map()
for row in gl_map:
if row.account == "_Test Cash - _TC":
self.assertEqual(row.debit, 100 * -1)
self.assertEqual(row.debit_in_account_currency, 100 * -1)
self.assertEqual(row.debit_in_transaction_currency, 100 * -1)
gl_map = process_gl_map(gl_map, False)
for row in gl_map:
if row.account == "_Test Cash - _TC":
self.assertEqual(row.credit, 100)
self.assertEqual(row.credit_in_account_currency, 100)
self.assertEqual(row.credit_in_transaction_currency, 100)
def test_transaction_exchange_rate_on_journals(self):
jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable USD - _TC", 100, save=False)
jv.accounts[0].update({"debit_in_account_currency": 8500, "exchange_rate": 1})
jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD", "exchange_rate": 85})
jv.submit()
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": jv.name, "is_cancelled": 0},
fields=["account", "transaction_exchange_rate"],
order_by="account",
)
expected = [
{"account": "_Test Bank - _TC", "transaction_exchange_rate": 1.0},
{"account": "_Test Receivable USD - _TC", "transaction_exchange_rate": 85.0},
]
self.assertEqual(expected, actual)
def make_journal_entry( def make_journal_entry(
account1, account1,
@@ -563,6 +637,3 @@ def make_journal_entry(
jv.submit() jv.submit()
return jv return jv
test_records = frappe.get_test_records("Journal Entry")

View File

@@ -1,94 +0,0 @@
[
{
"cheque_date": "2013-03-14",
"cheque_no": "33",
"company": "_Test Company",
"doctype": "Journal Entry",
"accounts": [
{
"account": "Debtors - _TC",
"party_type": "Customer",
"party": "_Test Customer",
"credit_in_account_currency": 400.0,
"debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account",
"parentfield": "accounts",
"cost_center": "_Test Cost Center - _TC"
},
{
"account": "_Test Bank - _TC",
"credit_in_account_currency": 0.0,
"debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account",
"parentfield": "accounts",
"cost_center": "_Test Cost Center - _TC"
}
],
"naming_series": "_T-Journal Entry-",
"posting_date": "2013-02-14",
"user_remark": "test",
"voucher_type": "Bank Entry"
},
{
"cheque_date": "2013-02-14",
"cheque_no": "33",
"company": "_Test Company",
"doctype": "Journal Entry",
"accounts": [
{
"account": "_Test Payable - _TC",
"party_type": "Supplier",
"party": "_Test Supplier",
"credit_in_account_currency": 0.0,
"debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account",
"parentfield": "accounts",
"cost_center": "_Test Cost Center - _TC"
},
{
"account": "_Test Bank - _TC",
"credit_in_account_currency": 400.0,
"debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account",
"parentfield": "accounts",
"cost_center": "_Test Cost Center - _TC"
}
],
"naming_series": "_T-Journal Entry-",
"posting_date": "2013-02-14",
"user_remark": "test",
"voucher_type": "Bank Entry"
},
{
"cheque_date": "2013-02-14",
"cheque_no": "33",
"company": "_Test Company",
"doctype": "Journal Entry",
"accounts": [
{
"account": "Debtors - _TC",
"party_type": "Customer",
"party": "_Test Customer",
"credit_in_account_currency": 0.0,
"debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account",
"parentfield": "accounts",
"cost_center": "_Test Cost Center - _TC"
},
{
"account": "Sales - _TC",
"credit_in_account_currency": 400.0,
"debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account",
"parentfield": "accounts",
"cost_center": "_Test Cost Center - _TC"
}
],
"naming_series": "_T-Journal Entry-",
"posting_date": "2013-02-14",
"user_remark": "test",
"voucher_type": "Bank Entry"
}
]

View File

@@ -0,0 +1,81 @@
[["Journal Entry"]]
cheque_date = "2013-03-14"
cheque_no = "33"
company = "_Test Company"
naming_series = "_T-Journal Entry-"
posting_date = "2013-02-14"
user_remark = "test"
voucher_type = "Bank Entry"
[["Journal Entry".accounts]]
account = "Debtors - _TC"
party_type = "Customer"
party = "_Test Customer"
credit_in_account_currency = 400.0
debit_in_account_currency = 0.0
doctype = "Journal Entry Account"
parentfield = "accounts"
cost_center = "_Test Cost Center - _TC"
[["Journal Entry".accounts]]
account = "_Test Bank - _TC"
credit_in_account_currency = 0.0
debit_in_account_currency = 400.0
doctype = "Journal Entry Account"
parentfield = "accounts"
cost_center = "_Test Cost Center - _TC"
[["Journal Entry"]]
cheque_date = "2013-02-14"
cheque_no = "33"
company = "_Test Company"
naming_series = "_T-Journal Entry-"
posting_date = "2013-02-14"
user_remark = "test"
voucher_type = "Bank Entry"
[["Journal Entry".accounts]]
account = "_Test Payable - _TC"
party_type = "Supplier"
party = "_Test Supplier"
credit_in_account_currency = 0.0
debit_in_account_currency = 400.0
doctype = "Journal Entry Account"
parentfield = "accounts"
cost_center = "_Test Cost Center - _TC"
[["Journal Entry".accounts]]
account = "_Test Bank - _TC"
credit_in_account_currency = 400.0
debit_in_account_currency = 0.0
doctype = "Journal Entry Account"
parentfield = "accounts"
cost_center = "_Test Cost Center - _TC"
[["Journal Entry"]]
cheque_date = "2013-02-14"
cheque_no = "33"
company = "_Test Company"
naming_series = "_T-Journal Entry-"
posting_date = "2013-02-14"
user_remark = "test"
voucher_type = "Bank Entry"
[["Journal Entry".accounts]]
account = "Debtors - _TC"
party_type = "Customer"
party = "_Test Customer"
credit_in_account_currency = 0.0
debit_in_account_currency = 400.0
doctype = "Journal Entry Account"
parentfield = "accounts"
cost_center = "_Test Cost Center - _TC"
[["Journal Entry".accounts]]
account = "Sales - _TC"
credit_in_account_currency = 400.0
debit_in_account_currency = 0.0
doctype = "Journal Entry Account"
parentfield = "accounts"
cost_center = "_Test Cost Center - _TC"

View File

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

View File

@@ -3,14 +3,14 @@
import frappe import frappe
from frappe import qb from frappe import qb
from frappe.tests.utils import FrappeTestCase from frappe.tests import IntegrationTestCase
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.accounts.utils import run_ledger_health_checks from erpnext.accounts.utils import run_ledger_health_checks
class TestLedgerHealth(AccountsTestMixin, FrappeTestCase): class TestLedgerHealth(AccountsTestMixin, IntegrationTestCase):
def setUp(self): def setUp(self):
self.create_company() self.create_company()
self.create_customer() self.create_customer()

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