mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-17 08:35:00 +00:00
Merge pull request #40681 from barredterra/ruff
chore: switch to ruff for python formatting/linting
This commit is contained in:
@@ -35,3 +35,6 @@ eb9ee3f79b94e594fc6dfa4f6514580e125eee8c
|
||||
|
||||
# js formatting
|
||||
ec74a5e56617bbd76ac402451468fd4668af543d
|
||||
|
||||
# ruff formatting
|
||||
a308792ee7fda18a681e9181f4fd00b36385bc23
|
||||
|
||||
15
.github/helper/documentation.py
vendored
15
.github/helper/documentation.py
vendored
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
import requests
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
|
||||
WEBSITE_REPOS = [
|
||||
"erpnext_com",
|
||||
@@ -36,11 +36,7 @@ def is_documentation_link(word: str) -> bool:
|
||||
|
||||
|
||||
def contains_documentation_link(body: str) -> bool:
|
||||
return any(
|
||||
is_documentation_link(word)
|
||||
for line in body.splitlines()
|
||||
for word in line.split()
|
||||
)
|
||||
return any(is_documentation_link(word) for line in body.splitlines() for word in line.split())
|
||||
|
||||
|
||||
def check_pull_request(number: str) -> "tuple[int, str]":
|
||||
@@ -53,12 +49,7 @@ def check_pull_request(number: str) -> "tuple[int, str]":
|
||||
head_sha = (payload.get("head") or {}).get("sha")
|
||||
body = (payload.get("body") or "").lower()
|
||||
|
||||
if (
|
||||
not title.startswith("feat")
|
||||
or not head_sha
|
||||
or "no-docs" in body
|
||||
or "backport" in body
|
||||
):
|
||||
if not title.startswith("feat") or not head_sha or "no-docs" in body or "backport" in body:
|
||||
return 0, "Skipping documentation checks... 🏃"
|
||||
|
||||
if contains_documentation_link(body):
|
||||
|
||||
34
.github/helper/translation.py
vendored
34
.github/helper/translation.py
vendored
@@ -2,7 +2,9 @@ import re
|
||||
import sys
|
||||
|
||||
errors_encounter = 0
|
||||
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
|
||||
pattern = re.compile(
|
||||
r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)"
|
||||
)
|
||||
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]")
|
||||
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}")
|
||||
f_string_pattern = re.compile(r"_\(f[\"']")
|
||||
@@ -10,14 +12,14 @@ starts_with_f_pattern = re.compile(r"_\(f")
|
||||
|
||||
# skip first argument
|
||||
files = sys.argv[1:]
|
||||
files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))]
|
||||
files_to_scan = [_file for _file in files if _file.endswith((".py", ".js"))]
|
||||
|
||||
for _file in files_to_scan:
|
||||
with open(_file, 'r') as f:
|
||||
print(f'Checking: {_file}')
|
||||
with open(_file) as f:
|
||||
print(f"Checking: {_file}")
|
||||
file_lines = f.readlines()
|
||||
for line_number, line in enumerate(file_lines, 1):
|
||||
if 'frappe-lint: disable-translate' in line:
|
||||
if "frappe-lint: disable-translate" in line:
|
||||
continue
|
||||
|
||||
start_matches = start_pattern.search(line)
|
||||
@@ -28,7 +30,9 @@ for _file in files_to_scan:
|
||||
has_f_string = f_string_pattern.search(line)
|
||||
if has_f_string:
|
||||
errors_encounter += 1
|
||||
print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}')
|
||||
print(
|
||||
f"\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}"
|
||||
)
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
@@ -36,25 +40,29 @@ for _file in files_to_scan:
|
||||
match = pattern.search(line)
|
||||
error_found = False
|
||||
|
||||
if not match and line.endswith((',\n', '[\n')):
|
||||
if not match and line.endswith((",\n", "[\n")):
|
||||
# concat remaining text to validate multiline pattern
|
||||
line = "".join(file_lines[line_number - 1:])
|
||||
line = line[start_matches.start() + 1:]
|
||||
line = "".join(file_lines[line_number - 1 :])
|
||||
line = line[start_matches.start() + 1 :]
|
||||
match = pattern.match(line)
|
||||
|
||||
if not match:
|
||||
error_found = True
|
||||
print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}')
|
||||
print(f"\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}")
|
||||
|
||||
if not error_found and not words_pattern.search(line):
|
||||
error_found = True
|
||||
print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}')
|
||||
print(
|
||||
f"\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}"
|
||||
)
|
||||
|
||||
if error_found:
|
||||
errors_encounter += 1
|
||||
|
||||
if errors_encounter > 0:
|
||||
print('\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.')
|
||||
print(
|
||||
'\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.'
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('\nGood To Go!')
|
||||
print("\nGood To Go!")
|
||||
|
||||
14
.github/workflows/patch.yml
vendored
14
.github/workflows/patch.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check for valid Python & Merge Conflicts
|
||||
run: |
|
||||
@@ -43,12 +43,12 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Setup Python
|
||||
uses: "actions/setup-python@v4"
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
check-latest: true
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v4
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
||||
18
.github/workflows/server-tests-mariadb.yml
vendored
18
.github/workflows/server-tests-mariadb.yml
vendored
@@ -54,12 +54,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Check for valid Python & Merge Conflicts
|
||||
run: |
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
check-latest: true
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v4
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -144,13 +144,13 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Upload coverage data
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
name: MariaDB
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
14
.github/workflows/server-tests-postgres.yml
vendored
14
.github/workflows/server-tests-postgres.yml
vendored
@@ -41,12 +41,12 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Check for valid Python & Merge Conflicts
|
||||
run: |
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
check-latest: true
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v4
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
||||
@@ -55,28 +55,15 @@ repos:
|
||||
erpnext/templates/includes/.*
|
||||
)$
|
||||
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.2.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies: [
|
||||
'flake8-bugbear',
|
||||
'flake8-tuple',
|
||||
]
|
||||
args: ['--config', '.github/helper/.flake8_strict']
|
||||
exclude: ".*setup.py$"
|
||||
- id: ruff
|
||||
name: "Run ruff linter and apply fixes"
|
||||
args: ["--fix"]
|
||||
|
||||
- repo: https://github.com/adityahase/black
|
||||
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
|
||||
hooks:
|
||||
- id: black
|
||||
additional_dependencies: ['click==8.0.4']
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
exclude: ".*setup.py$"
|
||||
- id: ruff-format
|
||||
name: "Format Python code"
|
||||
|
||||
|
||||
ci:
|
||||
|
||||
@@ -37,9 +37,7 @@ def get_default_cost_center(company):
|
||||
if not frappe.flags.company_cost_center:
|
||||
frappe.flags.company_cost_center = {}
|
||||
if company not in frappe.flags.company_cost_center:
|
||||
frappe.flags.company_cost_center[company] = frappe.get_cached_value(
|
||||
"Company", company, "cost_center"
|
||||
)
|
||||
frappe.flags.company_cost_center[company] = frappe.get_cached_value("Company", company, "cost_center")
|
||||
return frappe.flags.company_cost_center[company]
|
||||
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@ class ERPNextAddress(Address):
|
||||
def validate(self):
|
||||
self.validate_reference()
|
||||
self.update_compnay_address()
|
||||
super(ERPNextAddress, self).validate()
|
||||
super().validate()
|
||||
|
||||
def link_address(self):
|
||||
"""Link address based on owner"""
|
||||
if self.is_your_company_address:
|
||||
return
|
||||
|
||||
return super(ERPNextAddress, self).link_address()
|
||||
return super().link_address()
|
||||
|
||||
def update_compnay_address(self):
|
||||
for link in self.get("links"):
|
||||
@@ -26,11 +26,11 @@ class ERPNextAddress(Address):
|
||||
self.is_your_company_address = 1
|
||||
|
||||
def validate_reference(self):
|
||||
if self.is_your_company_address and not [
|
||||
row for row in self.links if row.link_doctype == "Company"
|
||||
]:
|
||||
if self.is_your_company_address and not [row for row in self.links if row.link_doctype == "Company"]:
|
||||
frappe.throw(
|
||||
_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
|
||||
_(
|
||||
"Address needs to be linked to a Company. Please add a row for Company in the Links table."
|
||||
),
|
||||
title=_("Company Not Linked"),
|
||||
)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ def get(
|
||||
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
|
||||
|
||||
account = filters.get("account")
|
||||
company = filters.get("company")
|
||||
filters.get("company")
|
||||
|
||||
if not account and chart_name:
|
||||
frappe.throw(
|
||||
@@ -83,7 +83,6 @@ def build_result(account, dates, gl_entries):
|
||||
|
||||
# get balances in debit
|
||||
for entry in gl_entries:
|
||||
|
||||
# entry date is after the current pointer, so move the pointer forward
|
||||
while getdate(entry.posting_date) > result[date_index][0]:
|
||||
date_index += 1
|
||||
@@ -133,8 +132,6 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
|
||||
|
||||
dates = [get_period_ending(from_date, timegrain)]
|
||||
while getdate(dates[-1]) < getdate(to_date):
|
||||
date = get_period_ending(
|
||||
add_to_date(dates[-1], years=years, months=months, days=days), timegrain
|
||||
)
|
||||
date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain)
|
||||
dates.append(date)
|
||||
return dates
|
||||
|
||||
@@ -24,14 +24,10 @@ from erpnext.accounts.utils import get_account_currency
|
||||
def validate_service_stop_date(doc):
|
||||
"""Validates service_stop_date for Purchase Invoice and Sales Invoice"""
|
||||
|
||||
enable_check = (
|
||||
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
)
|
||||
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
|
||||
old_stop_dates = {}
|
||||
old_doc = frappe.db.get_all(
|
||||
"{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
|
||||
)
|
||||
old_doc = frappe.db.get_all(f"{doc.doctype} Item", {"parent": doc.name}, ["name", "service_stop_date"])
|
||||
|
||||
for d in old_doc:
|
||||
old_stop_dates[d.name] = d.service_stop_date or ""
|
||||
@@ -62,16 +58,14 @@ def build_conditions(process_type, account, company):
|
||||
)
|
||||
|
||||
if account:
|
||||
conditions += "AND %s='%s'" % (deferred_account, account)
|
||||
conditions += f"AND {deferred_account}='{account}'"
|
||||
elif company:
|
||||
conditions += f"AND p.company = {frappe.db.escape(company)}"
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def convert_deferred_expense_to_expense(
|
||||
deferred_process, start_date=None, end_date=None, conditions=""
|
||||
):
|
||||
def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=""):
|
||||
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||
|
||||
if not start_date:
|
||||
@@ -81,16 +75,14 @@ def convert_deferred_expense_to_expense(
|
||||
|
||||
# check for the purchase invoice for which GL entries has to be done
|
||||
invoices = frappe.db.sql_list(
|
||||
"""
|
||||
f"""
|
||||
select distinct item.parent
|
||||
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
|
||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
||||
and item.enable_deferred_expense = 1 and item.parent=p.name
|
||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||
{0}
|
||||
""".format(
|
||||
conditions
|
||||
),
|
||||
{conditions}
|
||||
""",
|
||||
(end_date, start_date),
|
||||
) # nosec
|
||||
|
||||
@@ -103,9 +95,7 @@ def convert_deferred_expense_to_expense(
|
||||
send_mail(deferred_process)
|
||||
|
||||
|
||||
def convert_deferred_revenue_to_income(
|
||||
deferred_process, start_date=None, end_date=None, conditions=""
|
||||
):
|
||||
def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=""):
|
||||
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||
|
||||
if not start_date:
|
||||
@@ -115,16 +105,14 @@ def convert_deferred_revenue_to_income(
|
||||
|
||||
# check for the sales invoice for which GL entries has to be done
|
||||
invoices = frappe.db.sql_list(
|
||||
"""
|
||||
f"""
|
||||
select distinct item.parent
|
||||
from `tabSales Invoice Item` item, `tabSales Invoice` p
|
||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
||||
and item.enable_deferred_revenue = 1 and item.parent=p.name
|
||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||
{0}
|
||||
""".format(
|
||||
conditions
|
||||
),
|
||||
{conditions}
|
||||
""",
|
||||
(end_date, start_date),
|
||||
) # nosec
|
||||
|
||||
@@ -243,9 +231,7 @@ def calculate_monthly_amount(
|
||||
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||
doc, item
|
||||
)
|
||||
base_amount = flt(
|
||||
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
|
||||
)
|
||||
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
|
||||
if account_currency == doc.company_currency:
|
||||
amount = base_amount
|
||||
else:
|
||||
@@ -265,17 +251,13 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
|
||||
if account_currency == doc.company_currency:
|
||||
amount = base_amount
|
||||
else:
|
||||
amount = flt(
|
||||
item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")
|
||||
)
|
||||
amount = flt(item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount"))
|
||||
else:
|
||||
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||
doc, item
|
||||
)
|
||||
|
||||
base_amount = flt(
|
||||
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
|
||||
)
|
||||
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
|
||||
if account_currency == doc.company_currency:
|
||||
amount = base_amount
|
||||
else:
|
||||
@@ -296,26 +278,22 @@ def get_already_booked_amount(doc, item):
|
||||
|
||||
gl_entries_details = frappe.db.sql(
|
||||
"""
|
||||
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
|
||||
select sum({}) as total_credit, sum({}) as total_credit_in_account_currency, voucher_detail_no
|
||||
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
|
||||
and is_cancelled = 0
|
||||
group by voucher_detail_no
|
||||
""".format(
|
||||
total_credit_debit, total_credit_debit_currency
|
||||
),
|
||||
""".format(total_credit_debit, total_credit_debit_currency),
|
||||
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
journal_entry_details = frappe.db.sql(
|
||||
"""
|
||||
SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
|
||||
SELECT sum(c.{}) as total_credit, sum(c.{}) as total_credit_in_account_currency, reference_detail_no
|
||||
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
|
||||
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
|
||||
and p.docstatus < 2 group by reference_detail_no
|
||||
""".format(
|
||||
total_credit_debit, total_credit_debit_currency
|
||||
),
|
||||
""".format(total_credit_debit, total_credit_debit_currency),
|
||||
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||
as_dict=True,
|
||||
)
|
||||
@@ -337,9 +315,7 @@ def get_already_booked_amount(doc, item):
|
||||
|
||||
|
||||
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
enable_check = (
|
||||
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
)
|
||||
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||
|
||||
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
|
||||
|
||||
@@ -440,9 +416,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
via_journal_entry = cint(
|
||||
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
|
||||
)
|
||||
submit_journal_entry = cint(
|
||||
frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")
|
||||
)
|
||||
submit_journal_entry = cint(frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries"))
|
||||
book_deferred_entries_based_on = frappe.db.get_singles_value(
|
||||
"Accounts Settings", "book_deferred_entries_based_on"
|
||||
)
|
||||
@@ -462,9 +436,7 @@ def process_deferred_accounting(posting_date=None):
|
||||
posting_date = today()
|
||||
|
||||
if not cint(
|
||||
frappe.db.get_singles_value(
|
||||
"Accounts Settings", "automatically_process_deferred_accounting_entry"
|
||||
)
|
||||
frappe.db.get_singles_value("Accounts Settings", "automatically_process_deferred_accounting_entry")
|
||||
):
|
||||
return
|
||||
|
||||
@@ -587,16 +559,13 @@ def book_revenue_via_journal_entry(
|
||||
deferred_process=None,
|
||||
submit="No",
|
||||
):
|
||||
|
||||
if amount == 0:
|
||||
return
|
||||
|
||||
journal_entry = frappe.new_doc("Journal Entry")
|
||||
journal_entry.posting_date = posting_date
|
||||
journal_entry.company = doc.company
|
||||
journal_entry.voucher_type = (
|
||||
"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
|
||||
)
|
||||
journal_entry.voucher_type = "Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
|
||||
journal_entry.process_deferred_accounting = deferred_process
|
||||
|
||||
debit_entry = {
|
||||
@@ -645,7 +614,6 @@ def book_revenue_via_journal_entry(
|
||||
|
||||
|
||||
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
||||
|
||||
if doctype == "Sales Invoice":
|
||||
credit_account, debit_account = frappe.db.get_value(
|
||||
"Sales Invoice Item",
|
||||
|
||||
@@ -88,12 +88,10 @@ class Account(NestedSet):
|
||||
if frappe.local.flags.ignore_update_nsm:
|
||||
return
|
||||
else:
|
||||
super(Account, self).on_update()
|
||||
super().on_update()
|
||||
|
||||
def onload(self):
|
||||
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||
"Accounts Settings", "frozen_accounts_modifier"
|
||||
)
|
||||
frozen_accounts_modifier = frappe.db.get_single_value("Accounts Settings", "frozen_accounts_modifier")
|
||||
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
|
||||
self.set_onload("can_freeze_account", True)
|
||||
|
||||
@@ -218,9 +216,7 @@ class Account(NestedSet):
|
||||
|
||||
def validate_root_company_and_sync_account_to_children(self):
|
||||
# ignore validation while creating new compnay or while syncing to child companies
|
||||
if (
|
||||
frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
|
||||
):
|
||||
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
|
||||
return
|
||||
ancestors = get_root_company(self.company)
|
||||
if ancestors:
|
||||
@@ -418,7 +414,7 @@ class Account(NestedSet):
|
||||
if self.check_gle_exists():
|
||||
throw(_("Account with existing transaction can not be deleted"))
|
||||
|
||||
super(Account, self).on_trash(True)
|
||||
super().on_trash(True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -426,9 +422,8 @@ class Account(NestedSet):
|
||||
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql(
|
||||
"""select name from tabAccount
|
||||
where is_group = 1 and docstatus != 2 and company = %s
|
||||
and %s like %s order by name limit %s offset %s"""
|
||||
% ("%s", searchfield, "%s", "%s", "%s"),
|
||||
where is_group = 1 and docstatus != 2 and company = {}
|
||||
and {} like {} order by name limit {} offset {}""".format("%s", searchfield, "%s", "%s", "%s"),
|
||||
(filters["company"], "%%%s%%" % txt, page_len, start),
|
||||
as_list=1,
|
||||
)
|
||||
@@ -594,7 +589,5 @@ def sync_update_account_number_in_child(
|
||||
if old_acc_number:
|
||||
filters["account_number"] = old_acc_number
|
||||
|
||||
for d in frappe.db.get_values(
|
||||
"Account", filters=filters, fieldname=["company", "name"], as_dict=True
|
||||
):
|
||||
for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True):
|
||||
update_account_number(d["name"], account_name, account_number, from_descendant=True)
|
||||
|
||||
@@ -31,7 +31,6 @@ def create_charts(
|
||||
"tax_rate",
|
||||
"account_currency",
|
||||
]:
|
||||
|
||||
account_number = cstr(child.get("account_number")).strip()
|
||||
account_name, account_name_in_db = add_suffix_if_duplicate(
|
||||
account_name, account_number, accounts
|
||||
@@ -39,7 +38,9 @@ def create_charts(
|
||||
|
||||
is_group = identify_is_group(child)
|
||||
report_type = (
|
||||
"Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
|
||||
"Balance Sheet"
|
||||
if root_type in ["Asset", "Liability", "Equity"]
|
||||
else "Profit and Loss"
|
||||
)
|
||||
|
||||
account = frappe.get_doc(
|
||||
@@ -141,7 +142,7 @@ def get_chart(chart_template, existing_company=None):
|
||||
for fname in os.listdir(path):
|
||||
fname = frappe.as_unicode(fname)
|
||||
if fname.endswith(".json"):
|
||||
with open(os.path.join(path, fname), "r") as f:
|
||||
with open(os.path.join(path, fname)) as f:
|
||||
chart = f.read()
|
||||
if chart and json.loads(chart).get("name") == chart_template:
|
||||
return json.loads(chart).get("tree")
|
||||
@@ -173,7 +174,7 @@ def get_charts_for_country(country, with_standard=False):
|
||||
for fname in os.listdir(path):
|
||||
fname = frappe.as_unicode(fname)
|
||||
if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"):
|
||||
with open(os.path.join(path, fname), "r") as f:
|
||||
with open(os.path.join(path, fname)) as f:
|
||||
_get_chart_name(f.read())
|
||||
|
||||
# if more than one charts, returned then add the standard
|
||||
@@ -249,7 +250,13 @@ def validate_bank_account(coa, bank_account):
|
||||
|
||||
def _get_account_names(account_master):
|
||||
for account_name, child in account_master.items():
|
||||
if account_name not in ["account_number", "account_type", "root_type", "is_group", "tax_rate"]:
|
||||
if account_name not in [
|
||||
"account_number",
|
||||
"account_type",
|
||||
"root_type",
|
||||
"is_group",
|
||||
"tax_rate",
|
||||
]:
|
||||
accounts.append(account_name)
|
||||
|
||||
_get_account_names(child)
|
||||
|
||||
@@ -261,28 +261,20 @@ class TestAccount(unittest.TestCase):
|
||||
acc.insert()
|
||||
|
||||
self.assertTrue(
|
||||
frappe.db.exists(
|
||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}
|
||||
)
|
||||
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 4"})
|
||||
)
|
||||
self.assertTrue(
|
||||
frappe.db.exists(
|
||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
||||
)
|
||||
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 5"})
|
||||
)
|
||||
|
||||
# Try renaming child company account
|
||||
acc_tc_5 = frappe.db.get_value(
|
||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
||||
)
|
||||
self.assertRaises(
|
||||
frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account"
|
||||
)
|
||||
self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account")
|
||||
|
||||
# Rename child company account with allow_account_creation_against_child_company enabled
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 1
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1)
|
||||
|
||||
update_account_number(acc_tc_5, "Test Modified Account")
|
||||
self.assertTrue(
|
||||
@@ -291,9 +283,7 @@ class TestAccount(unittest.TestCase):
|
||||
)
|
||||
)
|
||||
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
|
||||
|
||||
to_delete = [
|
||||
"Test Group Account - _TC3",
|
||||
@@ -318,9 +308,7 @@ class TestAccount(unittest.TestCase):
|
||||
self.assertEqual(acc.account_currency, "INR")
|
||||
|
||||
# Make a JV against this account
|
||||
make_journal_entry(
|
||||
"Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True
|
||||
)
|
||||
make_journal_entry("Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True)
|
||||
|
||||
acc.account_currency = "USD"
|
||||
self.assertRaises(frappe.ValidationError, acc.save)
|
||||
|
||||
@@ -40,16 +40,12 @@ class AccountClosingBalance(Document):
|
||||
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
|
||||
accounting_dimensions = get_accounting_dimensions()
|
||||
|
||||
previous_closing_entries = get_previous_closing_entries(
|
||||
company, closing_date, accounting_dimensions
|
||||
)
|
||||
previous_closing_entries = get_previous_closing_entries(company, closing_date, accounting_dimensions)
|
||||
combined_entries = closing_entries + previous_closing_entries
|
||||
|
||||
merged_entries = aggregate_with_last_account_closing_balance(
|
||||
combined_entries, accounting_dimensions
|
||||
)
|
||||
merged_entries = aggregate_with_last_account_closing_balance(combined_entries, accounting_dimensions)
|
||||
|
||||
for key, value in merged_entries.items():
|
||||
for _key, value in merged_entries.items():
|
||||
cle = frappe.new_doc("Account Closing Balance")
|
||||
cle.update(value)
|
||||
cle.update(value["dimensions"])
|
||||
|
||||
@@ -40,7 +40,8 @@ class AccountingDimension(Document):
|
||||
self.set_fieldname_and_label()
|
||||
|
||||
def validate(self):
|
||||
if self.document_type in core_doctypes_list + (
|
||||
if self.document_type in (
|
||||
*core_doctypes_list,
|
||||
"Accounting Dimension",
|
||||
"Project",
|
||||
"Cost Center",
|
||||
@@ -48,13 +49,10 @@ class AccountingDimension(Document):
|
||||
"Company",
|
||||
"Account",
|
||||
):
|
||||
|
||||
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
||||
frappe.throw(msg)
|
||||
|
||||
exists = frappe.db.get_value(
|
||||
"Accounting Dimension", {"document_type": self.document_type}, ["name"]
|
||||
)
|
||||
exists = frappe.db.get_value("Accounting Dimension", {"document_type": self.document_type}, ["name"])
|
||||
|
||||
if exists and self.is_new():
|
||||
frappe.throw(_("Document Type already used as a dimension"))
|
||||
@@ -113,7 +111,6 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
||||
repostable_doctypes = get_allowed_types_from_settings()
|
||||
|
||||
for doctype in doclist:
|
||||
|
||||
if (doc_count + 1) % 2 == 0:
|
||||
insert_after_field = "dimension_col_break"
|
||||
else:
|
||||
@@ -148,7 +145,7 @@ def add_dimension_to_budget_doctype(df, doc):
|
||||
df.update(
|
||||
{
|
||||
"insert_after": "cost_center",
|
||||
"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type),
|
||||
"depends_on": f"eval:doc.budget_against == '{doc.document_type}'",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -182,19 +179,17 @@ def delete_accounting_dimension(doc):
|
||||
frappe.db.sql(
|
||||
"""
|
||||
DELETE FROM `tabCustom Field`
|
||||
WHERE fieldname = %s
|
||||
AND dt IN (%s)"""
|
||||
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname] + doclist),
|
||||
WHERE fieldname = {}
|
||||
AND dt IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname, *doclist]),
|
||||
)
|
||||
|
||||
frappe.db.sql(
|
||||
"""
|
||||
DELETE FROM `tabProperty Setter`
|
||||
WHERE field_name = %s
|
||||
AND doc_type IN (%s)"""
|
||||
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname] + doclist),
|
||||
WHERE field_name = {}
|
||||
AND doc_type IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||
tuple([doc.fieldname, *doclist]),
|
||||
)
|
||||
|
||||
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
|
||||
@@ -243,7 +238,6 @@ def get_doctypes_with_dimensions():
|
||||
|
||||
|
||||
def get_accounting_dimensions(as_list=True, filters=None):
|
||||
|
||||
if not filters:
|
||||
filters = {"disabled": 0}
|
||||
|
||||
@@ -274,7 +268,6 @@ def get_checks_for_pl_and_bs_accounts():
|
||||
|
||||
|
||||
def get_dimension_with_children(doctype, dimensions):
|
||||
|
||||
if isinstance(dimensions, str):
|
||||
dimensions = [dimensions]
|
||||
|
||||
@@ -282,9 +275,7 @@ def get_dimension_with_children(doctype, dimensions):
|
||||
|
||||
for dimension in dimensions:
|
||||
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
|
||||
children = frappe.get_all(
|
||||
doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft"
|
||||
)
|
||||
children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
|
||||
all_dimensions += [c.name for c in children]
|
||||
|
||||
return all_dimensions
|
||||
@@ -292,14 +283,10 @@ def get_dimension_with_children(doctype, dimensions):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_dimensions(with_cost_center_and_project=False):
|
||||
|
||||
c = frappe.qb.DocType("Accounting Dimension Detail")
|
||||
p = frappe.qb.DocType("Accounting Dimension")
|
||||
dimension_filters = (
|
||||
frappe.qb.from_(p)
|
||||
.select(p.label, p.fieldname, p.document_type)
|
||||
.where(p.disabled == 0)
|
||||
.run(as_dict=1)
|
||||
frappe.qb.from_(p).select(p.label, p.fieldname, p.document_type).where(p.disabled == 0).run(as_dict=1)
|
||||
)
|
||||
default_dimensions = (
|
||||
frappe.qb.from_(c)
|
||||
|
||||
@@ -57,9 +57,7 @@ class TestAccountingDimensionFilter(unittest.TestCase):
|
||||
|
||||
|
||||
def create_accounting_dimension_filter():
|
||||
if not frappe.db.get_value(
|
||||
"Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
|
||||
):
|
||||
if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Accounting Dimension Filter",
|
||||
|
||||
@@ -84,7 +84,10 @@ class AccountingPeriod(Document):
|
||||
for doctype_for_closing in self.get_doctypes_for_closing():
|
||||
self.append(
|
||||
"closed_documents",
|
||||
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
|
||||
{
|
||||
"document_type": doctype_for_closing.document_type,
|
||||
"closed": doctype_for_closing.closed,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -34,9 +34,7 @@ class TestAccountingPeriod(unittest.TestCase):
|
||||
ap1 = create_accounting_period(period_name="Test Accounting Period 2")
|
||||
ap1.save()
|
||||
|
||||
doc = create_sales_invoice(
|
||||
do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
|
||||
)
|
||||
doc = create_sales_invoice(do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.save)
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
@@ -37,11 +37,11 @@ class TestBankAccount(unittest.TestCase):
|
||||
try:
|
||||
bank_account.validate_iban()
|
||||
except ValidationError:
|
||||
msg = "BankAccount.validate_iban() failed for valid IBAN {}".format(iban)
|
||||
msg = f"BankAccount.validate_iban() failed for valid IBAN {iban}"
|
||||
self.fail(msg=msg)
|
||||
|
||||
for not_iban in invalid_ibans:
|
||||
bank_account.iban = not_iban
|
||||
msg = "BankAccount.validate_iban() accepted invalid IBAN {}".format(not_iban)
|
||||
msg = f"BankAccount.validate_iban() accepted invalid IBAN {not_iban}"
|
||||
with self.assertRaises(ValidationError, msg=msg):
|
||||
bank_account.validate_iban()
|
||||
|
||||
@@ -127,7 +127,7 @@ def get_payment_entries_for_bank_clearance(
|
||||
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
|
||||
|
||||
journal_entries = frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select
|
||||
"Journal Entry" as payment_document, t1.name as payment_entry,
|
||||
t1.cheque_no as cheque_number, t1.cheque_date,
|
||||
@@ -141,9 +141,7 @@ def get_payment_entries_for_bank_clearance(
|
||||
and ifnull(t1.is_opening, 'No') = 'No' {condition}
|
||||
group by t2.account, t1.name
|
||||
order by t1.posting_date ASC, t1.name DESC
|
||||
""".format(
|
||||
condition=condition
|
||||
),
|
||||
""",
|
||||
{"account": account, "from": from_date, "to": to_date},
|
||||
as_dict=1,
|
||||
)
|
||||
@@ -152,7 +150,7 @@ def get_payment_entries_for_bank_clearance(
|
||||
condition += "and bank_account = %(bank_account)s"
|
||||
|
||||
payment_entries = frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select
|
||||
"Payment Entry" as payment_document, name as payment_entry,
|
||||
reference_no as cheque_number, reference_date as cheque_date,
|
||||
@@ -167,9 +165,7 @@ def get_payment_entries_for_bank_clearance(
|
||||
{condition}
|
||||
order by
|
||||
posting_date ASC, name DESC
|
||||
""".format(
|
||||
condition=condition
|
||||
),
|
||||
""",
|
||||
{
|
||||
"account": account,
|
||||
"from": from_date,
|
||||
@@ -239,10 +235,7 @@ def get_payment_entries_for_bank_clearance(
|
||||
).run(as_dict=True)
|
||||
|
||||
entries = (
|
||||
list(payment_entries)
|
||||
+ list(journal_entries)
|
||||
+ list(pos_sales_invoices)
|
||||
+ list(pos_purchase_invoices)
|
||||
list(payment_entries) + list(journal_entries) + list(pos_sales_invoices) + list(pos_purchase_invoices)
|
||||
)
|
||||
|
||||
return entries
|
||||
|
||||
@@ -68,9 +68,7 @@ class TestBankClearance(unittest.TestCase):
|
||||
)
|
||||
loan.submit()
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=getdate())
|
||||
repayment_entry = create_repayment_entry(
|
||||
loan.name, "_Test Customer", getdate(), loan.loan_amount
|
||||
)
|
||||
repayment_entry = create_repayment_entry(loan.name, "_Test Customer", getdate(), loan.loan_amount)
|
||||
repayment_entry.save()
|
||||
repayment_entry.submit()
|
||||
|
||||
|
||||
@@ -81,9 +81,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
||||
def get_account_balance(bank_account, till_date):
|
||||
# returns account balance till the specified date
|
||||
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
||||
filters = frappe._dict(
|
||||
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
|
||||
)
|
||||
filters = frappe._dict({"account": account, "report_date": till_date, "include_pos_transactions": 1})
|
||||
data = get_entries(filters)
|
||||
|
||||
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
||||
@@ -96,10 +94,7 @@ def get_account_balance(bank_account, till_date):
|
||||
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
||||
|
||||
bank_bal = (
|
||||
flt(balance_as_per_system)
|
||||
- flt(total_debit)
|
||||
+ flt(total_credit)
|
||||
+ amounts_not_reflected_in_system
|
||||
flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
|
||||
)
|
||||
|
||||
return bank_bal
|
||||
@@ -538,9 +533,7 @@ def check_matching(
|
||||
for query in queries:
|
||||
matching_vouchers.extend(query.run(as_dict=True))
|
||||
|
||||
return (
|
||||
sorted(matching_vouchers, key=lambda x: x["rank"], reverse=True) if matching_vouchers else []
|
||||
)
|
||||
return sorted(matching_vouchers, key=lambda x: x["rank"], reverse=True) if matching_vouchers else []
|
||||
|
||||
|
||||
def get_queries(
|
||||
@@ -654,17 +647,13 @@ def get_bt_matching_query(exact_match, transaction):
|
||||
amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0)
|
||||
amount_condition = amount_equality if exact_match else getattr(bt, field) > 0.0
|
||||
|
||||
ref_rank = (
|
||||
frappe.qb.terms.Case().when(bt.reference_number == transaction.reference_number, 1).else_(0)
|
||||
)
|
||||
ref_rank = frappe.qb.terms.Case().when(bt.reference_number == transaction.reference_number, 1).else_(0)
|
||||
unallocated_rank = (
|
||||
frappe.qb.terms.Case().when(bt.unallocated_amount == transaction.unallocated_amount, 1).else_(0)
|
||||
)
|
||||
|
||||
party_condition = (
|
||||
(bt.party_type == transaction.party_type)
|
||||
& (bt.party == transaction.party)
|
||||
& bt.party.isnotnull()
|
||||
(bt.party_type == transaction.party_type) & (bt.party == transaction.party) & bt.party.isnotnull()
|
||||
)
|
||||
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||
|
||||
@@ -716,9 +705,7 @@ def get_pe_matching_query(
|
||||
amount_condition = amount_equality if exact_match else pe.paid_amount > 0.0
|
||||
|
||||
party_condition = (
|
||||
(pe.party_type == transaction.party_type)
|
||||
& (pe.party == transaction.party)
|
||||
& pe.party.isnotnull()
|
||||
(pe.party_type == transaction.party_type) & (pe.party == transaction.party) & pe.party.isnotnull()
|
||||
)
|
||||
party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0)
|
||||
|
||||
@@ -749,7 +736,7 @@ def get_pe_matching_query(
|
||||
.orderby(pe.reference_date if cint(filter_by_reference_date) else pe.posting_date)
|
||||
)
|
||||
|
||||
if frappe.flags.auto_reconcile_vouchers == True:
|
||||
if frappe.flags.auto_reconcile_vouchers is True:
|
||||
query = query.where(ref_condition)
|
||||
|
||||
return query
|
||||
@@ -810,7 +797,7 @@ def get_je_matching_query(
|
||||
.orderby(je.cheque_date if cint(filter_by_reference_date) else je.posting_date)
|
||||
)
|
||||
|
||||
if frappe.flags.auto_reconcile_vouchers == True:
|
||||
if frappe.flags.auto_reconcile_vouchers is True:
|
||||
query = query.where(ref_condition)
|
||||
|
||||
return query
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, flt, getdate, today
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import add_days, today
|
||||
|
||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||
auto_reconcile_vouchers,
|
||||
@@ -22,7 +21,7 @@ class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase):
|
||||
self.create_customer()
|
||||
self.clear_old_entries()
|
||||
bank_dt = qb.DocType("Bank")
|
||||
q = qb.from_(bank_dt).delete().where(bank_dt.name == "HDFC").run()
|
||||
qb.from_(bank_dt).delete().where(bank_dt.name == "HDFC").run()
|
||||
self.create_bank_account()
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
@@ -45,7 +45,7 @@ class BankStatementImport(DataImport):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BankStatementImport, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def validate(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
@@ -54,7 +54,6 @@ class BankStatementImport(DataImport):
|
||||
or (doc_before_save and doc_before_save.import_file != self.import_file)
|
||||
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
|
||||
):
|
||||
|
||||
template_options_dict = {}
|
||||
column_to_field_map = {}
|
||||
bank = frappe.get_doc("Bank", self.bank)
|
||||
@@ -69,7 +68,6 @@ class BankStatementImport(DataImport):
|
||||
self.validate_google_sheets_url()
|
||||
|
||||
def start_import(self):
|
||||
|
||||
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
|
||||
self.import_file, self.google_sheets_url
|
||||
)
|
||||
@@ -126,7 +124,7 @@ def download_errored_template(data_import_name):
|
||||
def parse_data_from_template(raw_data):
|
||||
data = []
|
||||
|
||||
for i, row in enumerate(raw_data):
|
||||
for _i, row in enumerate(raw_data):
|
||||
if all(v in INVALID_VALUES for v in row):
|
||||
# empty row
|
||||
continue
|
||||
@@ -136,9 +134,7 @@ def parse_data_from_template(raw_data):
|
||||
return data
|
||||
|
||||
|
||||
def start_import(
|
||||
data_import, bank_account, import_file_path, google_sheets_url, bank, template_options
|
||||
):
|
||||
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
|
||||
"""This method runs in background job"""
|
||||
|
||||
update_mapping_db(bank, template_options)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from typing import Tuple, Union
|
||||
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from rapidfuzz import fuzz, process
|
||||
@@ -19,7 +17,7 @@ class AutoMatchParty:
|
||||
def get(self, key):
|
||||
return self.__dict__.get(key, None)
|
||||
|
||||
def match(self) -> Union[Tuple, None]:
|
||||
def match(self) -> tuple | None:
|
||||
result = None
|
||||
result = AutoMatchbyAccountIBAN(
|
||||
bank_party_account_number=self.bank_party_account_number,
|
||||
@@ -50,7 +48,7 @@ class AutoMatchbyAccountIBAN:
|
||||
result = self.match_account_in_party()
|
||||
return result
|
||||
|
||||
def match_account_in_party(self) -> Union[Tuple, None]:
|
||||
def match_account_in_party(self) -> tuple | None:
|
||||
"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
|
||||
result = None
|
||||
parties = get_parties_in_order(self.deposit)
|
||||
@@ -97,7 +95,7 @@ class AutoMatchbyPartyNameDescription:
|
||||
def get(self, key):
|
||||
return self.__dict__.get(key, None)
|
||||
|
||||
def match(self) -> Union[Tuple, None]:
|
||||
def match(self) -> tuple | None:
|
||||
# fuzzy search by customer/supplier & employee
|
||||
if not (self.bank_party_name or self.description):
|
||||
return None
|
||||
@@ -105,7 +103,7 @@ class AutoMatchbyPartyNameDescription:
|
||||
result = self.match_party_name_desc_in_party()
|
||||
return result
|
||||
|
||||
def match_party_name_desc_in_party(self) -> Union[Tuple, None]:
|
||||
def match_party_name_desc_in_party(self) -> tuple | None:
|
||||
"""Fuzzy search party name and/or description against parties in the system"""
|
||||
result = None
|
||||
parties = get_parties_in_order(self.deposit)
|
||||
@@ -130,7 +128,7 @@ class AutoMatchbyPartyNameDescription:
|
||||
|
||||
return result
|
||||
|
||||
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
|
||||
def fuzzy_search_and_return_result(self, party, names, field) -> tuple | None:
|
||||
skip = False
|
||||
result = process.extract(
|
||||
query=self.get(field),
|
||||
@@ -147,7 +145,7 @@ class AutoMatchbyPartyNameDescription:
|
||||
party_name,
|
||||
), skip
|
||||
|
||||
def process_fuzzy_result(self, result: Union[list, None]):
|
||||
def process_fuzzy_result(self, result: list | None):
|
||||
"""
|
||||
If there are multiple valid close matches return None as result may be faulty.
|
||||
Return the result only if one accurate match stands out.
|
||||
|
||||
@@ -64,7 +64,9 @@ class BankTransaction(Document):
|
||||
_(
|
||||
"Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}"
|
||||
).format(
|
||||
frappe.bold(self.currency), frappe.bold(self.bank_account), frappe.bold(account_currency)
|
||||
frappe.bold(self.currency),
|
||||
frappe.bold(self.bank_account),
|
||||
frappe.bold(account_currency),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -180,7 +182,7 @@ class BankTransaction(Document):
|
||||
frappe.throw(_("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
|
||||
|
||||
for payment_entry in to_remove:
|
||||
self.remove(to_remove)
|
||||
self.remove(payment_entry)
|
||||
|
||||
@frappe.whitelist()
|
||||
def remove_payment_entries(self):
|
||||
@@ -235,9 +237,7 @@ def get_clearance_details(transaction, payment_entry):
|
||||
"""
|
||||
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
|
||||
bt_allocations = get_total_allocated_amount(
|
||||
payment_entry.payment_document, payment_entry.payment_entry
|
||||
)
|
||||
bt_allocations = get_total_allocated_amount(payment_entry.payment_document, payment_entry.payment_entry)
|
||||
|
||||
unallocated_amount = min(
|
||||
transaction.unallocated_amount,
|
||||
@@ -332,7 +332,6 @@ def get_total_allocated_amount(doctype, docname):
|
||||
|
||||
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
||||
|
||||
paid_amount_field = "paid_amount"
|
||||
if payment_entry.payment_document == "Payment Entry":
|
||||
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
|
||||
@@ -371,9 +370,7 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||
)
|
||||
|
||||
elif payment_entry.payment_document == "Loan Repayment":
|
||||
return frappe.db.get_value(
|
||||
payment_entry.payment_document, payment_entry.payment_entry, "amount_paid"
|
||||
)
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "amount_paid")
|
||||
|
||||
elif payment_entry.payment_document == "Bank Transaction":
|
||||
dep, wth = frappe.db.get_value(
|
||||
@@ -383,9 +380,7 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||
|
||||
else:
|
||||
frappe.throw(
|
||||
"Please reconcile {0}: {1} manually".format(
|
||||
payment_entry.payment_document, payment_entry.payment_entry
|
||||
)
|
||||
f"Please reconcile {payment_entry.payment_document}: {payment_entry.payment_entry} manually"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ def upload_bank_statement():
|
||||
fcontent = frappe.local.uploaded_file
|
||||
fname = frappe.local.uploaded_filename
|
||||
|
||||
if frappe.safe_encode(fname).lower().endswith("csv".encode("utf-8")):
|
||||
if frappe.safe_encode(fname).lower().endswith(b"csv"):
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
|
||||
rows = read_csv_content(fcontent, False)
|
||||
|
||||
elif frappe.safe_encode(fname).lower().endswith("xlsx".encode("utf-8")):
|
||||
elif frappe.safe_encode(fname).lower().endswith(b"xlsx"):
|
||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||
|
||||
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
||||
|
||||
@@ -436,9 +436,7 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
|
||||
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"}
|
||||
):
|
||||
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": gl_account})
|
||||
mode_of_payment.save()
|
||||
|
||||
|
||||
@@ -70,10 +70,11 @@ class Budget(Document):
|
||||
select
|
||||
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
|
||||
where
|
||||
ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and
|
||||
b.fiscal_year=%s and b.name != %s and ba.account in (%s) """
|
||||
% ("%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))),
|
||||
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts),
|
||||
ba.parent = b.name and b.docstatus < 2 and b.company = {} and {}={} and
|
||||
b.fiscal_year={} and b.name != {} and ba.account in ({}) """.format(
|
||||
"%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))
|
||||
),
|
||||
(self.company, budget_against, self.fiscal_year, self.name, *tuple(accounts)),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
@@ -96,12 +97,14 @@ class Budget(Document):
|
||||
if account_details.is_group:
|
||||
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
||||
elif account_details.company != self.company:
|
||||
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
|
||||
frappe.throw(
|
||||
_("Account {0} does not belongs to company {1}").format(d.account, self.company)
|
||||
)
|
||||
elif account_details.report_type != "Profit and Loss":
|
||||
frappe.throw(
|
||||
_("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format(
|
||||
d.account
|
||||
)
|
||||
_(
|
||||
"Budget cannot be assigned against {0}, as it's not an Income or Expense account"
|
||||
).format(d.account)
|
||||
)
|
||||
|
||||
if d.account in account_list:
|
||||
@@ -148,9 +151,7 @@ def validate_expense_against_budget(args, expense_amount=0):
|
||||
"Company", args.get("company"), "exception_budget_approver_role"
|
||||
)
|
||||
|
||||
if not frappe.get_cached_value(
|
||||
"Budget", {"fiscal_year": args.fiscal_year, "company": args.company}
|
||||
): # nosec
|
||||
if not frappe.get_cached_value("Budget", {"fiscal_year": args.fiscal_year, "company": args.company}): # nosec
|
||||
return
|
||||
|
||||
if not args.account:
|
||||
@@ -181,30 +182,24 @@ def validate_expense_against_budget(args, expense_amount=0):
|
||||
and args.account
|
||||
and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense")
|
||||
):
|
||||
|
||||
doctype = dimension.get("document_type")
|
||||
|
||||
if frappe.get_cached_value("DocType", doctype, "is_tree"):
|
||||
lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||
condition = """and exists(select name from `tab%s`
|
||||
where lft<=%s and rgt>=%s and name=b.%s)""" % (
|
||||
doctype,
|
||||
lft,
|
||||
rgt,
|
||||
budget_against,
|
||||
) # nosec
|
||||
condition = f"""and exists(select name from `tab{doctype}`
|
||||
where lft<={lft} and rgt>={rgt} and name=b.{budget_against})""" # nosec
|
||||
args.is_tree = True
|
||||
else:
|
||||
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
|
||||
condition = f"and b.{budget_against}={frappe.db.escape(args.get(budget_against))}"
|
||||
args.is_tree = False
|
||||
|
||||
args.budget_against_field = budget_against
|
||||
args.budget_against_doctype = doctype
|
||||
|
||||
budget_records = frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select
|
||||
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||
b.{budget_against} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
||||
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
||||
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
||||
@@ -217,9 +212,7 @@ def validate_expense_against_budget(args, expense_amount=0):
|
||||
b.name=ba.parent and b.fiscal_year=%s
|
||||
and ba.account=%s and b.docstatus=1
|
||||
{condition}
|
||||
""".format(
|
||||
condition=condition, budget_against_field=budget_against
|
||||
),
|
||||
""",
|
||||
(args.fiscal_year, args.account),
|
||||
as_dict=True,
|
||||
) # nosec
|
||||
@@ -247,7 +240,12 @@ def validate_budget_records(args, budget_records, expense_amount):
|
||||
args["month_end_date"] = get_last_day(args.posting_date)
|
||||
|
||||
compare_expense_with_budget(
|
||||
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
|
||||
args,
|
||||
budget_amount,
|
||||
_("Accumulated Monthly"),
|
||||
monthly_action,
|
||||
budget.budget_against,
|
||||
amount,
|
||||
)
|
||||
|
||||
|
||||
@@ -275,9 +273,8 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
||||
frappe.bold(fmt_money(diff, currency=currency)),
|
||||
)
|
||||
|
||||
if (
|
||||
frappe.flags.exception_approver_role
|
||||
and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)
|
||||
if frappe.flags.exception_approver_role and frappe.flags.exception_approver_role in frappe.get_roles(
|
||||
frappe.session.user
|
||||
):
|
||||
action = "Warn"
|
||||
|
||||
@@ -323,10 +320,8 @@ def get_requested_amount(args, budget):
|
||||
data = frappe.db.sql(
|
||||
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
|
||||
from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and
|
||||
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and
|
||||
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(
|
||||
condition
|
||||
),
|
||||
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {} and
|
||||
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition),
|
||||
item_code,
|
||||
as_list=1,
|
||||
)
|
||||
@@ -339,12 +334,10 @@ def get_ordered_amount(args, budget):
|
||||
condition = get_other_condition(args, budget, "Purchase Order")
|
||||
|
||||
data = frappe.db.sql(
|
||||
""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
||||
f""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
||||
from `tabPurchase Order Item` child, `tabPurchase Order` parent where
|
||||
parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt
|
||||
and parent.status != 'Closed' and {0}""".format(
|
||||
condition
|
||||
),
|
||||
and parent.status != 'Closed' and {condition}""",
|
||||
item_code,
|
||||
as_list=1,
|
||||
)
|
||||
@@ -357,7 +350,7 @@ def get_other_condition(args, budget, for_doc):
|
||||
budget_against_field = args.get("budget_against_field")
|
||||
|
||||
if budget_against_field and args.get(budget_against_field):
|
||||
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
|
||||
condition += f" and child.{budget_against_field} = '{args.get(budget_against_field)}'"
|
||||
|
||||
if args.get("fiscal_year"):
|
||||
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
|
||||
@@ -365,12 +358,8 @@ def get_other_condition(args, budget, for_doc):
|
||||
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
||||
)
|
||||
|
||||
condition += """ and parent.%s
|
||||
between '%s' and '%s' """ % (
|
||||
date_field,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
condition += f""" and parent.{date_field}
|
||||
between '{start_date}' and '{end_date}' """
|
||||
|
||||
return condition
|
||||
|
||||
@@ -389,21 +378,17 @@ def get_actual_expense(args):
|
||||
|
||||
args.update(lft_rgt)
|
||||
|
||||
condition2 = """and exists(select name from `tab{doctype}`
|
||||
condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
|
||||
where lft>=%(lft)s and rgt<=%(rgt)s
|
||||
and name=gle.{budget_against_field})""".format(
|
||||
doctype=args.budget_against_doctype, budget_against_field=budget_against_field # nosec
|
||||
)
|
||||
and name=gle.{budget_against_field})"""
|
||||
else:
|
||||
condition2 = """and exists(select name from `tab{doctype}`
|
||||
where name=gle.{budget_against} and
|
||||
gle.{budget_against} = %({budget_against})s)""".format(
|
||||
doctype=args.budget_against_doctype, budget_against=budget_against_field
|
||||
)
|
||||
condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
|
||||
where name=gle.{budget_against_field} and
|
||||
gle.{budget_against_field} = %({budget_against_field})s)"""
|
||||
|
||||
amount = flt(
|
||||
frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select sum(gle.debit) - sum(gle.credit)
|
||||
from `tabGL Entry` gle
|
||||
where
|
||||
@@ -414,9 +399,7 @@ def get_actual_expense(args):
|
||||
and gle.company=%(company)s
|
||||
and gle.docstatus=1
|
||||
{condition2}
|
||||
""".format(
|
||||
condition1=condition1, condition2=condition2
|
||||
),
|
||||
""",
|
||||
(args),
|
||||
)[0][0]
|
||||
) # nosec
|
||||
|
||||
@@ -41,9 +41,7 @@ class TestBudget(unittest.TestCase):
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -63,9 +61,7 @@ class TestBudget(unittest.TestCase):
|
||||
|
||||
budget = make_budget(budget_against="Cost Center")
|
||||
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -97,9 +93,7 @@ class TestBudget(unittest.TestCase):
|
||||
)
|
||||
|
||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||
|
||||
mr = frappe.get_doc(
|
||||
@@ -138,9 +132,7 @@ class TestBudget(unittest.TestCase):
|
||||
)
|
||||
|
||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||
|
||||
po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
|
||||
@@ -158,9 +150,7 @@ class TestBudget(unittest.TestCase):
|
||||
|
||||
budget = make_budget(budget_against="Project")
|
||||
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
|
||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||
|
||||
@@ -223,7 +213,7 @@ class TestBudget(unittest.TestCase):
|
||||
if month > 9:
|
||||
month = 9
|
||||
|
||||
for i in range(month + 1):
|
||||
for _i in range(month + 1):
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC",
|
||||
@@ -237,9 +227,7 @@ class TestBudget(unittest.TestCase):
|
||||
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
||||
)
|
||||
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
|
||||
self.assertRaises(BudgetError, jv.cancel)
|
||||
|
||||
@@ -255,7 +243,7 @@ class TestBudget(unittest.TestCase):
|
||||
month = 9
|
||||
|
||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||
for i in range(month + 1):
|
||||
for _i in range(month + 1):
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC",
|
||||
@@ -270,9 +258,7 @@ class TestBudget(unittest.TestCase):
|
||||
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
||||
)
|
||||
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
|
||||
self.assertRaises(BudgetError, jv.cancel)
|
||||
|
||||
@@ -284,9 +270,7 @@ class TestBudget(unittest.TestCase):
|
||||
set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
|
||||
|
||||
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -316,9 +300,7 @@ class TestBudget(unittest.TestCase):
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
|
||||
frappe.db.set_value(
|
||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
||||
)
|
||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -423,13 +405,11 @@ def make_budget(**args):
|
||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||
|
||||
if budget_against == "Project":
|
||||
project_name = "{0}%".format("_Test Project/" + fiscal_year)
|
||||
project_name = "{}%".format("_Test Project/" + fiscal_year)
|
||||
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
|
||||
else:
|
||||
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
|
||||
budget_list = frappe.get_all(
|
||||
"Budget", fields=["name"], filters={"name": ("like", cost_center_name)}
|
||||
)
|
||||
cost_center_name = "{}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
|
||||
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", cost_center_name)})
|
||||
for d in budget_list:
|
||||
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
|
||||
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
|
||||
@@ -451,24 +431,18 @@ def make_budget(**args):
|
||||
budget.action_if_annual_budget_exceeded = "Stop"
|
||||
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
|
||||
budget.budget_against = budget_against
|
||||
budget.append(
|
||||
"accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000}
|
||||
)
|
||||
budget.append("accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000})
|
||||
|
||||
if args.applicable_on_material_request:
|
||||
budget.applicable_on_material_request = 1
|
||||
budget.action_if_annual_budget_exceeded_on_mr = (
|
||||
args.action_if_annual_budget_exceeded_on_mr or "Warn"
|
||||
)
|
||||
budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or "Warn"
|
||||
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
|
||||
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
|
||||
)
|
||||
|
||||
if args.applicable_on_purchase_order:
|
||||
budget.applicable_on_purchase_order = 1
|
||||
budget.action_if_annual_budget_exceeded_on_po = (
|
||||
args.action_if_annual_budget_exceeded_on_po or "Warn"
|
||||
)
|
||||
budget.action_if_annual_budget_exceeded_on_po = args.action_if_annual_budget_exceeded_on_po or "Warn"
|
||||
budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
|
||||
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
|
||||
)
|
||||
|
||||
@@ -38,9 +38,7 @@ class ChartofAccountsImporter(Document):
|
||||
|
||||
def validate(self):
|
||||
if self.import_file:
|
||||
get_coa(
|
||||
"Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1
|
||||
)
|
||||
get_coa("Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1)
|
||||
|
||||
|
||||
def validate_columns(data):
|
||||
@@ -116,7 +114,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
|
||||
file_path = file_doc.get_full_path()
|
||||
|
||||
data = []
|
||||
with open(file_path, "r") as in_file:
|
||||
with open(file_path) as in_file:
|
||||
csv_reader = list(csv.reader(in_file))
|
||||
headers = csv_reader[0]
|
||||
del csv_reader[0] # delete top row and headers row
|
||||
@@ -215,10 +213,10 @@ def build_forest(data):
|
||||
for row in data:
|
||||
account_name, parent_account, account_number, parent_account_number = row[0:4]
|
||||
if account_number:
|
||||
account_name = "{} - {}".format(account_number, account_name)
|
||||
account_name = f"{account_number} - {account_name}"
|
||||
if parent_account_number:
|
||||
parent_account_number = cstr(parent_account_number).strip()
|
||||
parent_account = "{} - {}".format(parent_account_number, parent_account)
|
||||
parent_account = f"{parent_account_number} - {parent_account}"
|
||||
|
||||
if parent_account == account_name == child:
|
||||
return [parent_account]
|
||||
@@ -230,7 +228,7 @@ def build_forest(data):
|
||||
frappe.bold(parent_account)
|
||||
)
|
||||
)
|
||||
return [child] + parent_account_list
|
||||
return [child, *parent_account_list]
|
||||
|
||||
charts_map, paths = {}, []
|
||||
|
||||
@@ -250,12 +248,12 @@ def build_forest(data):
|
||||
) = i
|
||||
|
||||
if not account_name:
|
||||
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
||||
error_messages.append(f"Row {line_no}: Please enter Account Name")
|
||||
|
||||
name = account_name
|
||||
if account_number:
|
||||
account_number = cstr(account_number).strip()
|
||||
account_name = "{} - {}".format(account_number, account_name)
|
||||
account_name = f"{account_number} - {account_name}"
|
||||
|
||||
charts_map[account_name] = {}
|
||||
charts_map[account_name]["account_name"] = name
|
||||
@@ -352,9 +350,9 @@ def get_template(template_type, company):
|
||||
|
||||
def get_sample_template(writer, company):
|
||||
currency = frappe.db.get_value("Company", company, "default_currency")
|
||||
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f:
|
||||
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv")) as f:
|
||||
for row in f:
|
||||
row = row.strip().split(",") + [currency]
|
||||
row = [*row.strip().split(","), currency]
|
||||
writer.writerow(row)
|
||||
|
||||
return writer
|
||||
@@ -463,7 +461,7 @@ def unset_existing_data(company):
|
||||
"Purchase Taxes and Charges Template",
|
||||
]:
|
||||
frappe.db.sql(
|
||||
'''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company) # nosec
|
||||
f'''delete from `tab{doctype}` where `company`="%s"''' % (company) # nosec
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -66,71 +66,71 @@ def create_or_update_cheque_print_format(template_name):
|
||||
|
||||
cheque_print.html = """
|
||||
<style>
|
||||
.print-format {
|
||||
.print-format {{
|
||||
padding: 0px;
|
||||
}
|
||||
@media screen {
|
||||
.print-format {
|
||||
}}
|
||||
@media screen {{
|
||||
.print-format {{
|
||||
padding: 0in;
|
||||
}
|
||||
}
|
||||
}}
|
||||
}}
|
||||
</style>
|
||||
<div style="position: relative; top:%(starting_position_from_top_edge)scm">
|
||||
<div style="width:%(cheque_width)scm;height:%(cheque_height)scm;">
|
||||
<span style="top:%(acc_pay_dist_from_top_edge)scm; left:%(acc_pay_dist_from_left_edge)scm;
|
||||
<div style="position: relative; top:{starting_position_from_top_edge}cm">
|
||||
<div style="width:{cheque_width}cm;height:{cheque_height}cm;">
|
||||
<span style="top:{acc_pay_dist_from_top_edge}cm; left:{acc_pay_dist_from_left_edge}cm;
|
||||
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
|
||||
%(message_to_show)s
|
||||
{message_to_show}
|
||||
</span>
|
||||
<span style="top:%(date_dist_from_top_edge)scm; left:%(date_dist_from_left_edge)scm;
|
||||
<span style="top:{date_dist_from_top_edge}cm; left:{date_dist_from_left_edge}cm;
|
||||
position: absolute;">
|
||||
{{ frappe.utils.formatdate(doc.reference_date) or '' }}
|
||||
{{{{ frappe.utils.formatdate(doc.reference_date) or '' }}}}
|
||||
</span>
|
||||
<span style="top:%(acc_no_dist_from_top_edge)scm;left:%(acc_no_dist_from_left_edge)scm;
|
||||
<span style="top:{acc_no_dist_from_top_edge}cm;left:{acc_no_dist_from_left_edge}cm;
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{ doc.account_no or '' }}
|
||||
{{{{ doc.account_no or '' }}}}
|
||||
</span>
|
||||
<span style="top:%(payer_name_from_top_edge)scm;left: %(payer_name_from_left_edge)scm;
|
||||
<span style="top:{payer_name_from_top_edge}cm;left: {payer_name_from_left_edge}cm;
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{doc.party_name}}
|
||||
{{{{doc.party_name}}}}
|
||||
</span>
|
||||
<span style="top:%(amt_in_words_from_top_edge)scm; left:%(amt_in_words_from_left_edge)scm;
|
||||
position: absolute; display: block; width: %(amt_in_word_width)scm;
|
||||
line-height:%(amt_in_words_line_spacing)scm; word-wrap: break-word;">
|
||||
{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}
|
||||
<span style="top:{amt_in_words_from_top_edge}cm; left:{amt_in_words_from_left_edge}cm;
|
||||
position: absolute; display: block; width: {amt_in_word_width}cm;
|
||||
line-height:{amt_in_words_line_spacing}cm; word-wrap: break-word;">
|
||||
{{{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}}}
|
||||
</span>
|
||||
<span style="top:%(amt_in_figures_from_top_edge)scm;left: %(amt_in_figures_from_left_edge)scm;
|
||||
<span style="top:{amt_in_figures_from_top_edge}cm;left: {amt_in_figures_from_left_edge}cm;
|
||||
position: absolute; min-width: 4cm;">
|
||||
{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}
|
||||
{{{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}}}
|
||||
</span>
|
||||
<span style="top:%(signatory_from_top_edge)scm;left: %(signatory_from_left_edge)scm;
|
||||
<span style="top:{signatory_from_top_edge}cm;left: {signatory_from_left_edge}cm;
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{doc.company}}
|
||||
{{{{doc.company}}}}
|
||||
</span>
|
||||
</div>
|
||||
</div>""" % {
|
||||
"starting_position_from_top_edge": doc.starting_position_from_top_edge
|
||||
</div>""".format(
|
||||
starting_position_from_top_edge=doc.starting_position_from_top_edge
|
||||
if doc.cheque_size == "A4"
|
||||
else 0.0,
|
||||
"cheque_width": doc.cheque_width,
|
||||
"cheque_height": doc.cheque_height,
|
||||
"acc_pay_dist_from_top_edge": doc.acc_pay_dist_from_top_edge,
|
||||
"acc_pay_dist_from_left_edge": doc.acc_pay_dist_from_left_edge,
|
||||
"message_to_show": doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
|
||||
"date_dist_from_top_edge": doc.date_dist_from_top_edge,
|
||||
"date_dist_from_left_edge": doc.date_dist_from_left_edge,
|
||||
"acc_no_dist_from_top_edge": doc.acc_no_dist_from_top_edge,
|
||||
"acc_no_dist_from_left_edge": doc.acc_no_dist_from_left_edge,
|
||||
"payer_name_from_top_edge": doc.payer_name_from_top_edge,
|
||||
"payer_name_from_left_edge": doc.payer_name_from_left_edge,
|
||||
"amt_in_words_from_top_edge": doc.amt_in_words_from_top_edge,
|
||||
"amt_in_words_from_left_edge": doc.amt_in_words_from_left_edge,
|
||||
"amt_in_word_width": doc.amt_in_word_width,
|
||||
"amt_in_words_line_spacing": doc.amt_in_words_line_spacing,
|
||||
"amt_in_figures_from_top_edge": doc.amt_in_figures_from_top_edge,
|
||||
"amt_in_figures_from_left_edge": doc.amt_in_figures_from_left_edge,
|
||||
"signatory_from_top_edge": doc.signatory_from_top_edge,
|
||||
"signatory_from_left_edge": doc.signatory_from_left_edge,
|
||||
}
|
||||
cheque_width=doc.cheque_width,
|
||||
cheque_height=doc.cheque_height,
|
||||
acc_pay_dist_from_top_edge=doc.acc_pay_dist_from_top_edge,
|
||||
acc_pay_dist_from_left_edge=doc.acc_pay_dist_from_left_edge,
|
||||
message_to_show=doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
|
||||
date_dist_from_top_edge=doc.date_dist_from_top_edge,
|
||||
date_dist_from_left_edge=doc.date_dist_from_left_edge,
|
||||
acc_no_dist_from_top_edge=doc.acc_no_dist_from_top_edge,
|
||||
acc_no_dist_from_left_edge=doc.acc_no_dist_from_left_edge,
|
||||
payer_name_from_top_edge=doc.payer_name_from_top_edge,
|
||||
payer_name_from_left_edge=doc.payer_name_from_left_edge,
|
||||
amt_in_words_from_top_edge=doc.amt_in_words_from_top_edge,
|
||||
amt_in_words_from_left_edge=doc.amt_in_words_from_left_edge,
|
||||
amt_in_word_width=doc.amt_in_word_width,
|
||||
amt_in_words_line_spacing=doc.amt_in_words_line_spacing,
|
||||
amt_in_figures_from_top_edge=doc.amt_in_figures_from_top_edge,
|
||||
amt_in_figures_from_left_edge=doc.amt_in_figures_from_left_edge,
|
||||
signatory_from_top_edge=doc.signatory_from_top_edge,
|
||||
signatory_from_left_edge=doc.signatory_from_left_edge,
|
||||
)
|
||||
|
||||
cheque_print.save(ignore_permissions=True)
|
||||
|
||||
|
||||
@@ -34,9 +34,7 @@ class CostCenter(NestedSet):
|
||||
def autoname(self):
|
||||
from erpnext.accounts.utils import get_autoname_with_number
|
||||
|
||||
self.name = get_autoname_with_number(
|
||||
self.cost_center_number, self.cost_center_name, self.company
|
||||
)
|
||||
self.name = get_autoname_with_number(self.cost_center_number, self.cost_center_name, self.company)
|
||||
|
||||
def validate(self):
|
||||
self.validate_mandatory()
|
||||
@@ -109,14 +107,14 @@ class CostCenter(NestedSet):
|
||||
new_cost_center = get_name_with_abbr(newdn, self.company)
|
||||
|
||||
# Validate properties before merging
|
||||
super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "is_group")
|
||||
super().before_rename(olddn, new_cost_center, merge, "is_group")
|
||||
if not merge:
|
||||
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
|
||||
|
||||
return new_cost_center
|
||||
|
||||
def after_rename(self, olddn, newdn, merge=False):
|
||||
super(CostCenter, self).after_rename(olddn, newdn, merge)
|
||||
super().after_rename(olddn, newdn, merge)
|
||||
|
||||
if not merge:
|
||||
new_cost_center = frappe.db.get_value(
|
||||
|
||||
@@ -10,7 +10,6 @@ test_records = frappe.get_test_records("Cost Center")
|
||||
|
||||
class TestCostCenter(unittest.TestCase):
|
||||
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()
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class CostCenterAllocation(Document):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CostCenterAllocation, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._skip_from_date_validation = False
|
||||
|
||||
def validate(self):
|
||||
@@ -63,9 +63,7 @@ class CostCenterAllocation(Document):
|
||||
total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
|
||||
|
||||
if total_percentage != 100:
|
||||
frappe.throw(
|
||||
_("Total percentage against cost centers should be 100"), WrongPercentageAllocation
|
||||
)
|
||||
frappe.throw(_("Total percentage against cost centers should be 100"), WrongPercentageAllocation)
|
||||
|
||||
def validate_from_date_based_on_existing_gle(self):
|
||||
# Check if GLE exists against the main cost center
|
||||
|
||||
@@ -43,7 +43,6 @@ class CurrencyExchangeSettings(Document):
|
||||
|
||||
def set_parameters_and_result(self):
|
||||
if self.service_provider == "exchangerate.host":
|
||||
|
||||
if not self.access_key:
|
||||
frappe.throw(
|
||||
_("Access Key is required for Service Provider: {0}").format(
|
||||
@@ -78,9 +77,7 @@ class CurrencyExchangeSettings(Document):
|
||||
transaction_date=nowdate(), to_currency="INR", from_currency="USD"
|
||||
)
|
||||
|
||||
api_url = self.api_endpoint.format(
|
||||
transaction_date=nowdate(), to_currency="INR", from_currency="USD"
|
||||
)
|
||||
api_url = self.api_endpoint.format(transaction_date=nowdate(), to_currency="INR", from_currency="USD")
|
||||
|
||||
try:
|
||||
response = requests.get(api_url, params=params)
|
||||
@@ -100,14 +97,14 @@ class CurrencyExchangeSettings(Document):
|
||||
]
|
||||
except Exception:
|
||||
frappe.throw(_("Invalid result key. Response:") + " " + response.text)
|
||||
if not isinstance(value, (int, float)):
|
||||
if not isinstance(value, int | float):
|
||||
frappe.throw(_("Returned exchange rate is neither integer not float."))
|
||||
|
||||
self.url = response.url
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_api_endpoint(service_provider: str = None, use_http: bool = False):
|
||||
def get_api_endpoint(service_provider: str | None = None, use_http: bool = False):
|
||||
if service_provider and service_provider in ["exchangerate.host", "frankfurter.app"]:
|
||||
if service_provider == "exchangerate.host":
|
||||
api = "api.exchangerate.host/convert"
|
||||
|
||||
@@ -109,9 +109,7 @@ class TestDunning(FrappeTestCase):
|
||||
|
||||
def create_dunning(overdue_days, dunning_type_name=None):
|
||||
posting_date = add_days(today(), -1 * overdue_days)
|
||||
sales_invoice = create_sales_invoice_against_cost_center(
|
||||
posting_date=posting_date, qty=1, rate=100
|
||||
)
|
||||
sales_invoice = create_sales_invoice_against_cost_center(posting_date=posting_date, qty=1, rate=100)
|
||||
dunning = create_dunning_from_sales_invoice(sales_invoice.name)
|
||||
|
||||
if dunning_type_name:
|
||||
|
||||
@@ -268,7 +268,6 @@ class ExchangeRateRevaluation(Document):
|
||||
|
||||
# Handle Accounts with '0' balance in Account/Base Currency
|
||||
for d in [x for x in account_details if x.zero_balance]:
|
||||
|
||||
if d.balance != 0:
|
||||
current_exchange_rate = new_exchange_rate = 0
|
||||
|
||||
@@ -281,7 +280,8 @@ class ExchangeRateRevaluation(Document):
|
||||
new_balance_in_account_currency = 0
|
||||
|
||||
current_exchange_rate = (
|
||||
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0
|
||||
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party)
|
||||
or 0.0
|
||||
)
|
||||
|
||||
gain_loss = new_balance_in_account_currency - (
|
||||
@@ -335,9 +335,7 @@ class ExchangeRateRevaluation(Document):
|
||||
|
||||
revaluation_jv = self.make_jv_for_revaluation()
|
||||
if revaluation_jv:
|
||||
frappe.msgprint(
|
||||
f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}"
|
||||
)
|
||||
frappe.msgprint(f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}")
|
||||
|
||||
return {
|
||||
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
|
||||
@@ -394,7 +392,8 @@ class ExchangeRateRevaluation(Document):
|
||||
journal_account.update(
|
||||
{
|
||||
dr_or_cr: flt(
|
||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||
abs(d.get("balance_in_account_currency")),
|
||||
d.precision("balance_in_account_currency"),
|
||||
),
|
||||
reverse_dr_or_cr: 0,
|
||||
"debit": 0,
|
||||
@@ -520,7 +519,9 @@ class ExchangeRateRevaluation(Document):
|
||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||
),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
|
||||
"exchange_rate": flt(
|
||||
d.get("current_exchange_rate"), d.precision("current_exchange_rate")
|
||||
),
|
||||
"reference_type": "Exchange Rate Revaluation",
|
||||
"reference_name": self.name,
|
||||
}
|
||||
@@ -598,7 +599,7 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_account_details(
|
||||
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float = None
|
||||
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float | None = None
|
||||
):
|
||||
if not (company and posting_date):
|
||||
frappe.throw(_("Company and Posting Date is mandatory"))
|
||||
@@ -611,7 +612,7 @@ def get_account_details(
|
||||
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
||||
|
||||
account_details = {}
|
||||
company_currency = erpnext.get_company_currency(company)
|
||||
erpnext.get_company_currency(company)
|
||||
|
||||
account_details = {
|
||||
"account_currency": account_currency,
|
||||
@@ -625,9 +626,7 @@ def get_account_details(
|
||||
rounding_loss_allowance=rounding_loss_allowance,
|
||||
)
|
||||
|
||||
if account_balance and (
|
||||
account_balance[0].balance or account_balance[0].balance_in_account_currency
|
||||
):
|
||||
if account_balance and (account_balance[0].balance or account_balance[0].balance_in_account_currency):
|
||||
if account_with_new_balance := ExchangeRateRevaluation.calculate_new_account_balance(
|
||||
company, posting_date, account_balance
|
||||
):
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, flt, today
|
||||
|
||||
from erpnext import get_default_cost_center
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.party import get_party_account
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
|
||||
|
||||
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
@@ -73,9 +66,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
err.extend("accounts", accounts)
|
||||
row = err.accounts[0]
|
||||
row.new_exchange_rate = 85
|
||||
row.new_balance_in_base_currency = flt(
|
||||
row.new_exchange_rate * flt(row.balance_in_account_currency)
|
||||
)
|
||||
row.new_balance_in_base_currency = flt(row.new_exchange_rate * flt(row.balance_in_account_currency))
|
||||
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
|
||||
err.set_total_gain_loss()
|
||||
err = err.save().submit()
|
||||
@@ -127,9 +118,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
pe.save().submit()
|
||||
|
||||
# Cancel the auto created gain/loss JE to simulate balance only in base currency
|
||||
je = frappe.db.get_all(
|
||||
"Journal Entry Account", filters={"reference_name": si.name}, pluck="parent"
|
||||
)[0]
|
||||
je = frappe.db.get_all("Journal Entry Account", filters={"reference_name": si.name}, pluck="parent")[
|
||||
0
|
||||
]
|
||||
frappe.get_doc("Journal Entry", je).cancel()
|
||||
|
||||
err = frappe.new_doc("Exchange Rate Revaluation")
|
||||
@@ -235,9 +226,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
self.assertEqual(flt(acc.debit, precision), 0.0)
|
||||
self.assertEqual(flt(acc.credit, precision), 0.0)
|
||||
|
||||
row = [x for x in je.accounts if x.account == self.debtors_usd][0]
|
||||
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
|
||||
row = [x for x in je.accounts if x.account != self.debtors_usd][0]
|
||||
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
|
||||
|
||||
# total_debit and total_credit will be 0.0, as JV is posting only to account currency fields
|
||||
@@ -294,5 +285,5 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||
"new_balance_in_account_currency": 100.0,
|
||||
}
|
||||
|
||||
for key, val in expected_data.items():
|
||||
for key, _val in expected_data.items():
|
||||
self.assertEqual(expected_data.get(key), account_details.get(key))
|
||||
|
||||
@@ -108,9 +108,9 @@ class FiscalYear(Document):
|
||||
|
||||
if overlap:
|
||||
frappe.throw(
|
||||
_("Year start date or end date is overlapping with {0}. To avoid please set company").format(
|
||||
existing.name
|
||||
),
|
||||
_(
|
||||
"Year start date or end date is overlapping with {0}. To avoid please set company"
|
||||
).format(existing.name),
|
||||
frappe.NameError,
|
||||
)
|
||||
|
||||
@@ -126,9 +126,9 @@ def check_duplicate_fiscal_year(doc):
|
||||
not frappe.flags.in_test
|
||||
):
|
||||
frappe.throw(
|
||||
_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(
|
||||
fiscal_year
|
||||
)
|
||||
_(
|
||||
"Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}"
|
||||
).format(fiscal_year)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -105,13 +105,18 @@ class GLEntry(Document):
|
||||
]:
|
||||
# Update outstanding amt on against voucher
|
||||
if (
|
||||
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
||||
self.against_voucher_type
|
||||
in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
|
||||
and self.against_voucher
|
||||
and self.flags.update_outstanding == "Yes"
|
||||
and not frappe.flags.is_reverse_depr_entry
|
||||
):
|
||||
update_outstanding_amt(
|
||||
self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher
|
||||
self.account,
|
||||
self.party_type,
|
||||
self.party,
|
||||
self.against_voucher_type,
|
||||
self.against_voucher,
|
||||
)
|
||||
|
||||
def check_mandatory(self):
|
||||
@@ -180,9 +185,9 @@ class GLEntry(Document):
|
||||
):
|
||||
if not self.get(dimension.fieldname):
|
||||
frappe.throw(
|
||||
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
|
||||
dimension.label, self.account
|
||||
)
|
||||
_(
|
||||
"Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}."
|
||||
).format(dimension.label, self.account)
|
||||
)
|
||||
|
||||
if (
|
||||
@@ -193,9 +198,9 @@ class GLEntry(Document):
|
||||
):
|
||||
if not self.get(dimension.fieldname):
|
||||
frappe.throw(
|
||||
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
|
||||
dimension.label, self.account
|
||||
)
|
||||
_(
|
||||
"Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}."
|
||||
).format(dimension.label, self.account)
|
||||
)
|
||||
|
||||
def check_pl_account(self):
|
||||
@@ -243,9 +248,7 @@ class GLEntry(Document):
|
||||
if not self.cost_center:
|
||||
return
|
||||
|
||||
is_group, company = frappe.get_cached_value(
|
||||
"Cost Center", self.cost_center, ["is_group", "company"]
|
||||
)
|
||||
is_group, company = frappe.get_cached_value("Cost Center", self.cost_center, ["is_group", "company"])
|
||||
|
||||
if company != self.company:
|
||||
frappe.throw(
|
||||
@@ -314,7 +317,7 @@ def update_outstanding_amt(
|
||||
account, party_type, party, against_voucher_type, against_voucher, on_cancel=False
|
||||
):
|
||||
if party_type and party:
|
||||
party_condition = " and party_type={0} and party={1}".format(
|
||||
party_condition = " and party_type={} and party={}".format(
|
||||
frappe.db.escape(party_type), frappe.db.escape(party)
|
||||
)
|
||||
else:
|
||||
@@ -322,23 +325,19 @@ def update_outstanding_amt(
|
||||
|
||||
if against_voucher_type == "Sales Invoice":
|
||||
party_account = frappe.get_cached_value(against_voucher_type, against_voucher, "debit_to")
|
||||
account_condition = "and account in ({0}, {1})".format(
|
||||
frappe.db.escape(account), frappe.db.escape(party_account)
|
||||
)
|
||||
account_condition = f"and account in ({frappe.db.escape(account)}, {frappe.db.escape(party_account)})"
|
||||
else:
|
||||
account_condition = " and account = {0}".format(frappe.db.escape(account))
|
||||
account_condition = f" and account = {frappe.db.escape(account)}"
|
||||
|
||||
# get final outstanding amt
|
||||
bal = flt(
|
||||
frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
from `tabGL Entry`
|
||||
where against_voucher_type=%s and against_voucher=%s
|
||||
and voucher_type != 'Invoice Discounting'
|
||||
{0} {1}""".format(
|
||||
party_condition, account_condition
|
||||
),
|
||||
{party_condition} {account_condition}""",
|
||||
(against_voucher_type, against_voucher),
|
||||
)[0][0]
|
||||
or 0.0
|
||||
@@ -349,12 +348,10 @@ def update_outstanding_amt(
|
||||
elif against_voucher_type == "Journal Entry":
|
||||
against_voucher_amount = flt(
|
||||
frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
|
||||
and account = %s and (against_voucher is null or against_voucher='') {0}""".format(
|
||||
party_condition
|
||||
),
|
||||
and account = %s and (against_voucher is null or against_voucher='') {party_condition}""",
|
||||
(against_voucher, account),
|
||||
)[0][0]
|
||||
)
|
||||
@@ -373,7 +370,9 @@ def update_outstanding_amt(
|
||||
# Validation : Outstanding can not be negative for JV
|
||||
if bal < 0 and not on_cancel:
|
||||
frappe.throw(
|
||||
_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))
|
||||
_("Outstanding for {0} cannot be less than zero ({1})").format(
|
||||
against_voucher, fmt_money(bal)
|
||||
)
|
||||
)
|
||||
|
||||
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
||||
@@ -446,7 +445,7 @@ def rename_temporarily_named_docs(doctype):
|
||||
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
||||
newname = doc.name
|
||||
frappe.db.sql(
|
||||
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
|
||||
f"UPDATE `tab{doctype}` SET name = %s, to_rename = 0 where name = %s",
|
||||
(newname, oldname),
|
||||
auto_commit=True,
|
||||
)
|
||||
|
||||
@@ -14,9 +14,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
|
||||
class TestGLEntry(unittest.TestCase):
|
||||
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_cost_center", "_Test Cost Center - _TC"
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")
|
||||
|
||||
jv = make_journal_entry(
|
||||
"_Test Account Cost for Goods Sold - _TC",
|
||||
@@ -73,7 +71,9 @@ class TestGLEntry(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(all(entry.to_rename == 0 for entry in new_gl_entries))
|
||||
|
||||
self.assertTrue(all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries)))
|
||||
self.assertTrue(
|
||||
all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries, strict=False))
|
||||
)
|
||||
|
||||
new_naming_series_current_value = frappe.db.sql(
|
||||
"SELECT current from tabSeries where name = %s", naming_series
|
||||
|
||||
@@ -83,9 +83,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}"
|
||||
).format(
|
||||
record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)
|
||||
)
|
||||
).format(record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice))
|
||||
)
|
||||
|
||||
def calculate_total_amount(self):
|
||||
@@ -105,7 +103,9 @@ class InvoiceDiscounting(AccountsController):
|
||||
self.status = status
|
||||
self.db_set("status", status)
|
||||
for d in self.invoices:
|
||||
frappe.get_doc("Sales Invoice", d.sales_invoice).set_status(update=True, update_modified=False)
|
||||
frappe.get_doc("Sales Invoice", d.sales_invoice).set_status(
|
||||
update=True, update_modified=False
|
||||
)
|
||||
else:
|
||||
self.status = "Draft"
|
||||
if self.docstatus == 1:
|
||||
|
||||
@@ -75,8 +75,8 @@ class TestInvoiceDiscounting(unittest.TestCase):
|
||||
gle = get_gl_entries("Invoice Discounting", inv_disc.name)
|
||||
|
||||
expected_gle = {inv.debit_to: [0.0, 200], self.ar_credit: [200, 0.0]}
|
||||
for i, gle in enumerate(gle):
|
||||
self.assertEqual([gle.debit, gle.credit], expected_gle.get(gle.account))
|
||||
for _i, gle_value in enumerate(gle):
|
||||
self.assertEqual([gle_value.debit, gle_value.credit], expected_gle.get(gle_value.account))
|
||||
|
||||
def test_loan_on_submit(self):
|
||||
inv = create_sales_invoice(rate=300)
|
||||
@@ -92,9 +92,7 @@ class TestInvoiceDiscounting(unittest.TestCase):
|
||||
period=60,
|
||||
)
|
||||
self.assertEqual(inv_disc.status, "Sanctioned")
|
||||
self.assertEqual(
|
||||
inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period)
|
||||
)
|
||||
self.assertEqual(inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period))
|
||||
|
||||
def test_on_disbursed(self):
|
||||
inv = create_sales_invoice(rate=500)
|
||||
@@ -262,13 +260,9 @@ class TestInvoiceDiscounting(unittest.TestCase):
|
||||
je_on_payment.submit()
|
||||
|
||||
self.assertEqual(je_on_payment.accounts[0].account, self.ar_discounted)
|
||||
self.assertEqual(
|
||||
je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
|
||||
)
|
||||
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
|
||||
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
|
||||
self.assertEqual(
|
||||
je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
|
||||
)
|
||||
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
|
||||
|
||||
inv.reload()
|
||||
self.assertEqual(inv.outstanding_amount, 0)
|
||||
@@ -304,13 +298,9 @@ class TestInvoiceDiscounting(unittest.TestCase):
|
||||
je_on_payment.submit()
|
||||
|
||||
self.assertEqual(je_on_payment.accounts[0].account, self.ar_unpaid)
|
||||
self.assertEqual(
|
||||
je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
|
||||
)
|
||||
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
|
||||
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
|
||||
self.assertEqual(
|
||||
je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
|
||||
)
|
||||
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
|
||||
|
||||
inv.reload()
|
||||
self.assertEqual(inv.outstanding_amount, 0)
|
||||
|
||||
@@ -32,7 +32,7 @@ class ItemTaxTemplate(Document):
|
||||
def autoname(self):
|
||||
if self.company and self.title:
|
||||
abbr = frappe.get_cached_value("Company", self.company, "abbr")
|
||||
self.name = "{0} - {1}".format(self.title, abbr)
|
||||
self.name = f"{self.title} - {abbr}"
|
||||
|
||||
def validate_tax_accounts(self):
|
||||
"""Check whether Tax Rate is not entered twice for same Tax Type"""
|
||||
|
||||
@@ -112,7 +112,7 @@ class JournalEntry(AccountsController):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(JournalEntry, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def validate(self):
|
||||
if self.voucher_type == "Opening Entry":
|
||||
@@ -191,7 +191,7 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def on_cancel(self):
|
||||
# References for this Journal are removed on the `on_cancel` event in accounts_controller
|
||||
super(JournalEntry, self).on_cancel()
|
||||
super().on_cancel()
|
||||
self.ignore_linked_doctypes = (
|
||||
"GL Entry",
|
||||
"Stock Ledger Entry",
|
||||
@@ -216,9 +216,9 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def update_advance_paid(self):
|
||||
advance_paid = frappe._dict()
|
||||
advance_payment_doctypes = frappe.get_hooks(
|
||||
"advance_payment_receivable_doctypes"
|
||||
) + frappe.get_hooks("advance_payment_payable_doctypes")
|
||||
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
|
||||
"advance_payment_payable_doctypes"
|
||||
)
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance:
|
||||
if d.reference_type in advance_payment_doctypes:
|
||||
@@ -229,10 +229,7 @@ class JournalEntry(AccountsController):
|
||||
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
|
||||
|
||||
def validate_inter_company_accounts(self):
|
||||
if (
|
||||
self.voucher_type == "Inter Company Journal Entry"
|
||||
and self.inter_company_journal_entry_reference
|
||||
):
|
||||
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
|
||||
doc = frappe.get_doc("Journal Entry", self.inter_company_journal_entry_reference)
|
||||
account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
|
||||
previous_account_currency = frappe.get_cached_value("Company", doc.company, "default_currency")
|
||||
@@ -378,10 +375,7 @@ class JournalEntry(AccountsController):
|
||||
asset.set_status()
|
||||
|
||||
def update_inter_company_jv(self):
|
||||
if (
|
||||
self.voucher_type == "Inter Company Journal Entry"
|
||||
and self.inter_company_journal_entry_reference
|
||||
):
|
||||
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
|
||||
frappe.db.set_value(
|
||||
"Journal Entry",
|
||||
self.inter_company_journal_entry_reference,
|
||||
@@ -409,17 +403,25 @@ class JournalEntry(AccountsController):
|
||||
if d.account == inv_disc_doc.short_term_loan and d.reference_name == inv_disc:
|
||||
if self.docstatus == 1:
|
||||
if d.credit > 0:
|
||||
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Sanctioned", d.idx)
|
||||
_validate_invoice_discounting_status(
|
||||
inv_disc, inv_disc_doc.status, "Sanctioned", d.idx
|
||||
)
|
||||
status = "Disbursed"
|
||||
elif d.debit > 0:
|
||||
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx)
|
||||
_validate_invoice_discounting_status(
|
||||
inv_disc, inv_disc_doc.status, "Disbursed", d.idx
|
||||
)
|
||||
status = "Settled"
|
||||
else:
|
||||
if d.credit > 0:
|
||||
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx)
|
||||
_validate_invoice_discounting_status(
|
||||
inv_disc, inv_disc_doc.status, "Disbursed", d.idx
|
||||
)
|
||||
status = "Sanctioned"
|
||||
elif d.debit > 0:
|
||||
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Settled", d.idx)
|
||||
_validate_invoice_discounting_status(
|
||||
inv_disc, inv_disc_doc.status, "Settled", d.idx
|
||||
)
|
||||
status = "Disbursed"
|
||||
break
|
||||
if status:
|
||||
@@ -488,10 +490,7 @@ class JournalEntry(AccountsController):
|
||||
)
|
||||
|
||||
def unlink_inter_company_jv(self):
|
||||
if (
|
||||
self.voucher_type == "Inter Company Journal Entry"
|
||||
and self.inter_company_journal_entry_reference
|
||||
):
|
||||
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
|
||||
frappe.db.set_value(
|
||||
"Journal Entry",
|
||||
self.inter_company_journal_entry_reference,
|
||||
@@ -513,9 +512,9 @@ class JournalEntry(AccountsController):
|
||||
if account_type in ["Receivable", "Payable"]:
|
||||
if not (d.party_type and d.party):
|
||||
frappe.throw(
|
||||
_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(
|
||||
d.idx, d.account
|
||||
)
|
||||
_(
|
||||
"Row {0}: Party Type and Party is required for Receivable / Payable account {1}"
|
||||
).format(d.idx, d.account)
|
||||
)
|
||||
elif (
|
||||
d.party_type
|
||||
@@ -580,16 +579,18 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def system_generated_gain_loss(self):
|
||||
return (
|
||||
self.voucher_type == "Exchange Gain Or Loss"
|
||||
and self.multi_currency
|
||||
and self.is_system_generated
|
||||
self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency and self.is_system_generated
|
||||
)
|
||||
|
||||
def validate_against_jv(self):
|
||||
for d in self.get("accounts"):
|
||||
if d.reference_type == "Journal Entry":
|
||||
account_root_type = frappe.get_cached_value("Account", d.account, "root_type")
|
||||
if account_root_type == "Asset" and flt(d.debit) > 0 and not self.system_generated_gain_loss():
|
||||
if (
|
||||
account_root_type == "Asset"
|
||||
and flt(d.debit) > 0
|
||||
and not self.system_generated_gain_loss()
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: For {1}, you can select reference document only if account gets credited"
|
||||
@@ -671,7 +672,9 @@ class JournalEntry(AccountsController):
|
||||
|
||||
if d.reference_type == "Purchase Order" and flt(d.credit) > 0:
|
||||
frappe.throw(
|
||||
_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type)
|
||||
_("Row {0}: Credit entry can not be linked with a {1}").format(
|
||||
d.idx, d.reference_type
|
||||
)
|
||||
)
|
||||
|
||||
# set totals
|
||||
@@ -693,7 +696,10 @@ class JournalEntry(AccountsController):
|
||||
|
||||
# check if party and account match
|
||||
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
if self.voucher_type in ("Deferred Revenue", "Deferred Expense") and d.reference_detail_no:
|
||||
if (
|
||||
self.voucher_type in ("Deferred Revenue", "Deferred Expense")
|
||||
and d.reference_detail_no
|
||||
):
|
||||
debit_or_credit = "Debit" if d.debit else "Credit"
|
||||
party_account = get_deferred_booking_accounts(
|
||||
d.reference_type, d.reference_detail_no, debit_or_credit
|
||||
@@ -702,7 +708,8 @@ class JournalEntry(AccountsController):
|
||||
else:
|
||||
if d.reference_type == "Sales Invoice":
|
||||
party_account = (
|
||||
get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
|
||||
get_party_account_based_on_invoice_discounting(d.reference_name)
|
||||
or against_voucher[1]
|
||||
)
|
||||
else:
|
||||
party_account = against_voucher[1]
|
||||
@@ -826,7 +833,9 @@ class JournalEntry(AccountsController):
|
||||
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
|
||||
if self.difference:
|
||||
frappe.throw(
|
||||
_("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference)
|
||||
_("Total Debit must be equal to Total Credit. The difference is {0}").format(
|
||||
self.difference
|
||||
)
|
||||
)
|
||||
|
||||
def set_total_debit_credit(self):
|
||||
@@ -890,7 +899,6 @@ class JournalEntry(AccountsController):
|
||||
and self.posting_date
|
||||
)
|
||||
):
|
||||
|
||||
ignore_exchange_rate = False
|
||||
if self.get("flags") and self.flags.get("ignore_exchange_rate"):
|
||||
ignore_exchange_rate = True
|
||||
@@ -1136,27 +1144,21 @@ class JournalEntry(AccountsController):
|
||||
self.validate_total_debit_and_credit()
|
||||
|
||||
def get_values(self):
|
||||
cond = (
|
||||
" and outstanding_amount <= {0}".format(self.write_off_amount)
|
||||
if flt(self.write_off_amount) > 0
|
||||
else ""
|
||||
)
|
||||
cond = f" and outstanding_amount <= {self.write_off_amount}" if flt(self.write_off_amount) > 0 else ""
|
||||
|
||||
if self.write_off_based_on == "Accounts Receivable":
|
||||
return frappe.db.sql(
|
||||
"""select name, debit_to as account, customer as party, outstanding_amount
|
||||
from `tabSales Invoice` where docstatus = 1 and company = %s
|
||||
and outstanding_amount > 0 %s"""
|
||||
% ("%s", cond),
|
||||
from `tabSales Invoice` where docstatus = 1 and company = {}
|
||||
and outstanding_amount > 0 {}""".format("%s", cond),
|
||||
self.company,
|
||||
as_dict=True,
|
||||
)
|
||||
elif self.write_off_based_on == "Accounts Payable":
|
||||
return frappe.db.sql(
|
||||
"""select name, credit_to as account, supplier as party, outstanding_amount
|
||||
from `tabPurchase Invoice` where docstatus = 1 and company = %s
|
||||
and outstanding_amount > 0 %s"""
|
||||
% ("%s", cond),
|
||||
from `tabPurchase Invoice` where docstatus = 1 and company = {}
|
||||
and outstanding_amount > 0 {}""".format("%s", cond),
|
||||
self.company,
|
||||
as_dict=True,
|
||||
)
|
||||
@@ -1267,7 +1269,7 @@ def get_payment_entry_against_order(
|
||||
"amount_field_bank": amount_field_bank,
|
||||
"amount": amount,
|
||||
"debit_in_account_currency": debit_in_account_currency,
|
||||
"remarks": "Advance Payment received against {0} {1}".format(dt, dn),
|
||||
"remarks": f"Advance Payment received against {dt} {dn}",
|
||||
"is_advance": "Yes",
|
||||
"bank_account": bank_account,
|
||||
"journal_entry": journal_entry,
|
||||
@@ -1306,7 +1308,7 @@ def get_payment_entry_against_invoice(
|
||||
"amount_field_bank": amount_field_bank,
|
||||
"amount": amount if amount else abs(ref_doc.outstanding_amount),
|
||||
"debit_in_account_currency": debit_in_account_currency,
|
||||
"remarks": "Payment received against {0} {1}. {2}".format(dt, dn, ref_doc.remarks),
|
||||
"remarks": f"Payment received against {dt} {dn}. {ref_doc.remarks}",
|
||||
"is_advance": "No",
|
||||
"bank_account": bank_account,
|
||||
"journal_entry": journal_entry,
|
||||
@@ -1332,9 +1334,7 @@ def get_payment_entry(ref_doc, args):
|
||||
)
|
||||
|
||||
je = frappe.new_doc("Journal Entry")
|
||||
je.update(
|
||||
{"voucher_type": "Bank Entry", "company": ref_doc.company, "remark": args.get("remarks")}
|
||||
)
|
||||
je.update({"voucher_type": "Bank Entry", "company": ref_doc.company, "remark": args.get("remarks")})
|
||||
|
||||
party_row = je.append(
|
||||
"accounts",
|
||||
@@ -1357,9 +1357,7 @@ def get_payment_entry(ref_doc, args):
|
||||
bank_row = je.append("accounts")
|
||||
|
||||
# Make it bank_details
|
||||
bank_account = get_default_bank_cash_account(
|
||||
ref_doc.company, "Bank", account=args.get("bank_account")
|
||||
)
|
||||
bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account"))
|
||||
if bank_account:
|
||||
bank_row.update(bank_account)
|
||||
# Modified to include the posting date for which the exchange rate is required.
|
||||
@@ -1399,7 +1397,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
return []
|
||||
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
SELECT jv.name, jv.posting_date, jv.user_remark
|
||||
FROM `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
|
||||
WHERE jv_detail.parent = jv.name
|
||||
@@ -1410,16 +1408,14 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
OR jv_detail.reference_type = ''
|
||||
)
|
||||
AND jv.docstatus = 1
|
||||
AND jv.`{0}` LIKE %(txt)s
|
||||
AND jv.`{searchfield}` LIKE %(txt)s
|
||||
ORDER BY jv.name DESC
|
||||
LIMIT %(limit)s offset %(offset)s
|
||||
""".format(
|
||||
searchfield
|
||||
),
|
||||
""",
|
||||
dict(
|
||||
account=filters.get("account"),
|
||||
party=cstr(filters.get("party")),
|
||||
txt="%{0}%".format(txt),
|
||||
txt=f"%{txt}%",
|
||||
offset=start,
|
||||
limit=page_len,
|
||||
),
|
||||
@@ -1441,19 +1437,15 @@ def get_outstanding(args):
|
||||
condition = " and party=%(party)s" if args.get("party") else ""
|
||||
|
||||
against_jv_amount = frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
|
||||
and (reference_type is null or reference_type = '')""".format(
|
||||
condition
|
||||
),
|
||||
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {condition}
|
||||
and (reference_type is null or reference_type = '')""",
|
||||
args,
|
||||
)
|
||||
|
||||
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
|
||||
amount_field = (
|
||||
"credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
|
||||
)
|
||||
amount_field = "credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
|
||||
return {amount_field: abs(against_jv_amount)}
|
||||
elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"):
|
||||
party_type = "Customer" if args.get("doctype") == "Sales Invoice" else "Supplier"
|
||||
@@ -1466,9 +1458,7 @@ def get_outstanding(args):
|
||||
|
||||
due_date = invoice.get("due_date")
|
||||
|
||||
exchange_rate = (
|
||||
invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
|
||||
)
|
||||
exchange_rate = invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
|
||||
|
||||
if args["doctype"] == "Sales Invoice":
|
||||
amount_field = (
|
||||
@@ -1506,9 +1496,7 @@ def get_party_account_and_currency(company, party_type, party):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_account_details_and_party_type(
|
||||
account, date, company, debit=None, credit=None, exchange_rate=None
|
||||
):
|
||||
def get_account_details_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
|
||||
"""Returns dict of account details and party type to be set in Journal Entry on selection of account."""
|
||||
if not frappe.has_permission("Account"):
|
||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||
|
||||
@@ -69,10 +69,8 @@ class TestJournalEntry(unittest.TestCase):
|
||||
|
||||
self.assertTrue(
|
||||
frappe.db.sql(
|
||||
"""select name from `tabJournal Entry Account`
|
||||
where reference_type = %s and reference_name = %s and {0}=400""".format(
|
||||
dr_or_cr
|
||||
),
|
||||
f"""select name from `tabJournal Entry Account`
|
||||
where reference_type = %s and reference_name = %s and {dr_or_cr}=400""",
|
||||
(submitted_voucher.doctype, submitted_voucher.name),
|
||||
)
|
||||
)
|
||||
@@ -84,9 +82,8 @@ class TestJournalEntry(unittest.TestCase):
|
||||
def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr):
|
||||
# Test advance paid field
|
||||
advance_paid = frappe.db.sql(
|
||||
"""select advance_paid from `tab%s`
|
||||
where name=%s"""
|
||||
% (test_voucher.doctype, "%s"),
|
||||
"""select advance_paid from `tab{}`
|
||||
where name={}""".format(test_voucher.doctype, "%s"),
|
||||
(test_voucher.name),
|
||||
)
|
||||
payment_against_order = base_jv.get("accounts")[0].get(dr_or_cr)
|
||||
@@ -159,9 +156,7 @@ class TestJournalEntry(unittest.TestCase):
|
||||
jv.cancel()
|
||||
|
||||
def test_multi_currency(self):
|
||||
jv = make_journal_entry(
|
||||
"_Test Bank USD - _TC", "_Test Bank - _TC", 100, exchange_rate=50, save=False
|
||||
)
|
||||
jv = make_journal_entry("_Test Bank USD - _TC", "_Test Bank - _TC", 100, exchange_rate=50, save=False)
|
||||
|
||||
jv.get("accounts")[1].credit_in_account_currency = 5000
|
||||
jv.submit()
|
||||
@@ -477,9 +472,7 @@ class TestJournalEntry(unittest.TestCase):
|
||||
query = query.select(gl[field])
|
||||
|
||||
query = query.where(
|
||||
(gl.voucher_type == "Journal Entry")
|
||||
& (gl.voucher_no == self.voucher_no)
|
||||
& (gl.is_cancelled == 0)
|
||||
(gl.voucher_type == "Journal Entry") & (gl.voucher_no == self.voucher_no) & (gl.is_cancelled == 0)
|
||||
).orderby(gl.account)
|
||||
|
||||
gl_entries = query.run(as_dict=True)
|
||||
|
||||
@@ -83,7 +83,10 @@ class TestLedgerMerge(unittest.TestCase):
|
||||
"account": "Indirect Income - _TC",
|
||||
"merge_accounts": [
|
||||
{"account": "Indirect Test Income - _TC", "account_name": "Indirect Test Income"},
|
||||
{"account": "Administrative Test Income - _TC", "account_name": "Administrative Test Income"},
|
||||
{
|
||||
"account": "Administrative Test Income - _TC",
|
||||
"account_name": "Administrative Test Income",
|
||||
},
|
||||
],
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
@@ -52,13 +52,11 @@ def get_loyalty_details(
|
||||
condition += " and expiry_date>='%s' " % expiry_date
|
||||
|
||||
loyalty_point_details = frappe.db.sql(
|
||||
"""select sum(loyalty_points) as loyalty_points,
|
||||
f"""select sum(loyalty_points) as loyalty_points,
|
||||
sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
|
||||
where customer=%s and loyalty_program=%s and posting_date <= %s
|
||||
{condition}
|
||||
group by customer""".format(
|
||||
condition=condition
|
||||
),
|
||||
group by customer""",
|
||||
(customer, loyalty_program, expiry_date),
|
||||
as_dict=1,
|
||||
)
|
||||
@@ -79,9 +77,7 @@ def get_loyalty_program_details_with_points(
|
||||
include_expired_entry=False,
|
||||
current_transaction_amount=0,
|
||||
):
|
||||
lp_details = get_loyalty_program_details(
|
||||
customer, loyalty_program, company=company, silent=silent
|
||||
)
|
||||
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||
lp_details.update(
|
||||
get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)
|
||||
|
||||
@@ -19,9 +19,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
create_records()
|
||||
|
||||
def test_loyalty_points_earned_single_tier(self):
|
||||
frappe.db.set_value(
|
||||
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
|
||||
)
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||
# create a new sales invoice
|
||||
si_original = create_sales_invoice_record()
|
||||
si_original.insert()
|
||||
@@ -69,9 +67,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
d.cancel()
|
||||
|
||||
def test_loyalty_points_earned_multiple_tier(self):
|
||||
frappe.db.set_value(
|
||||
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty"
|
||||
)
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
|
||||
# assign multiple tier program to the customer
|
||||
customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
|
||||
customer.loyalty_program = frappe.get_doc(
|
||||
@@ -128,9 +124,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
|
||||
def test_cancel_sales_invoice(self):
|
||||
"""cancelling the sales invoice should cancel the earned points"""
|
||||
frappe.db.set_value(
|
||||
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
|
||||
)
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||
# create a new sales invoice
|
||||
si = create_sales_invoice_record()
|
||||
si.insert()
|
||||
@@ -148,9 +142,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
self.assertEqual(True, (lpe is None))
|
||||
|
||||
def test_sales_invoice_return(self):
|
||||
frappe.db.set_value(
|
||||
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
|
||||
)
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||
# create a new sales invoice
|
||||
si_original = create_sales_invoice_record(2)
|
||||
si_original.conversion_rate = flt(1)
|
||||
@@ -346,9 +338,7 @@ def create_records():
|
||||
).insert()
|
||||
|
||||
# create item price
|
||||
if not frappe.db.exists(
|
||||
"Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}
|
||||
):
|
||||
if not frappe.db.exists("Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Item Price",
|
||||
|
||||
@@ -54,9 +54,7 @@ class MonthlyDistribution(Document):
|
||||
total = sum(flt(d.percentage_allocation) for d in self.get("percentages"))
|
||||
|
||||
if flt(total, 2) != 100.0:
|
||||
frappe.throw(
|
||||
_("Percentage Allocation should be equal to 100%") + " ({0}%)".format(str(flt(total, 2)))
|
||||
)
|
||||
frappe.throw(_("Percentage Allocation should be equal to 100%") + f" ({flt(total, 2)!s}%)")
|
||||
|
||||
|
||||
def get_periodwise_distribution_data(distribution_id, period_list, periodicity):
|
||||
|
||||
@@ -83,9 +83,7 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
|
||||
company = "_Test Opening Invoice Company"
|
||||
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
|
||||
|
||||
old_default_receivable_account = frappe.db.get_value(
|
||||
"Company", company, "default_receivable_account"
|
||||
)
|
||||
old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account")
|
||||
frappe.db.set_value("Company", company, "default_receivable_account", "")
|
||||
|
||||
if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"):
|
||||
@@ -121,9 +119,7 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
|
||||
self.assertTrue(error_log)
|
||||
|
||||
# teardown
|
||||
frappe.db.set_value(
|
||||
"Company", company, "default_receivable_account", old_default_receivable_account
|
||||
)
|
||||
frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
|
||||
|
||||
def test_renaming_of_invoice_using_invoice_number_field(self):
|
||||
company = "_Test Opening Invoice Company"
|
||||
@@ -169,7 +165,7 @@ def get_opening_invoice_creation_dict(**args):
|
||||
{
|
||||
"qty": 1.0,
|
||||
"outstanding_amount": 300,
|
||||
"party": args.get("party_1") or "_Test {0}".format(party),
|
||||
"party": args.get("party_1") or f"_Test {party}",
|
||||
"item_name": "Opening Item",
|
||||
"due_date": "2016-09-10",
|
||||
"posting_date": "2016-09-05",
|
||||
@@ -179,7 +175,7 @@ def get_opening_invoice_creation_dict(**args):
|
||||
{
|
||||
"qty": 2.0,
|
||||
"outstanding_amount": 250,
|
||||
"party": args.get("party_2") or "_Test {0} 1".format(party),
|
||||
"party": args.get("party_2") or f"_Test {party} 1",
|
||||
"item_name": "Opening Item",
|
||||
"due_date": "2016-09-10",
|
||||
"posting_date": "2016-09-05",
|
||||
|
||||
@@ -38,7 +38,10 @@ class PartyLink(Document):
|
||||
if existing_party_link:
|
||||
frappe.throw(
|
||||
_("{} {} is already linked with {} {}").format(
|
||||
self.primary_role, bold(self.primary_party), self.secondary_role, bold(self.secondary_party)
|
||||
self.primary_role,
|
||||
bold(self.primary_party),
|
||||
self.secondary_role,
|
||||
bold(self.secondary_party),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ class PaymentEntry(AccountsController):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PaymentEntry, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.is_new():
|
||||
self.setup_party_account_field()
|
||||
|
||||
@@ -247,7 +247,7 @@ class PaymentEntry(AccountsController):
|
||||
"Unreconcile Payment",
|
||||
"Unreconcile Payment Entries",
|
||||
)
|
||||
super(PaymentEntry, self).on_cancel()
|
||||
super().on_cancel()
|
||||
self.make_gl_entries(cancel=1)
|
||||
self.update_outstanding_amounts()
|
||||
self.update_advance_paid()
|
||||
@@ -357,9 +357,7 @@ class PaymentEntry(AccountsController):
|
||||
# If term based allocation is enabled, throw
|
||||
if (
|
||||
d.payment_term is None or d.payment_term == ""
|
||||
) and self.term_based_allocation_enabled_for_reference(
|
||||
d.reference_doctype, d.reference_name
|
||||
):
|
||||
) and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name):
|
||||
frappe.throw(
|
||||
_(
|
||||
"{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section"
|
||||
@@ -371,7 +369,9 @@ class PaymentEntry(AccountsController):
|
||||
# The reference has already been fully paid
|
||||
if not latest:
|
||||
frappe.throw(
|
||||
_("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name)
|
||||
_("{0} {1} has already been fully paid.").format(
|
||||
_(d.reference_doctype), d.reference_name
|
||||
)
|
||||
)
|
||||
# The reference has already been partly paid
|
||||
elif (
|
||||
@@ -395,14 +395,14 @@ class PaymentEntry(AccountsController):
|
||||
and latest.payment_term_outstanding
|
||||
and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding))
|
||||
)
|
||||
and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name)
|
||||
and self.term_based_allocation_enabled_for_reference(
|
||||
d.reference_doctype, d.reference_name
|
||||
)
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}"
|
||||
).format(
|
||||
d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term
|
||||
)
|
||||
).format(d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term)
|
||||
)
|
||||
|
||||
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
|
||||
@@ -445,7 +445,9 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
if self.party:
|
||||
if not self.contact_person:
|
||||
set_contact_details(self, party=frappe._dict({"name": self.party}), party_type=self.party_type)
|
||||
set_contact_details(
|
||||
self, party=frappe._dict({"name": self.party}), party_type=self.party_type
|
||||
)
|
||||
else:
|
||||
complete_contact_details(self)
|
||||
if not self.party_balance:
|
||||
@@ -527,7 +529,9 @@ class PaymentEntry(AccountsController):
|
||||
else:
|
||||
if ref_doc:
|
||||
if self.paid_from_account_currency == ref_doc.currency:
|
||||
self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")
|
||||
self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get(
|
||||
"conversion_rate"
|
||||
)
|
||||
|
||||
if not self.source_exchange_rate:
|
||||
self.source_exchange_rate = get_exchange_rate(
|
||||
@@ -564,7 +568,7 @@ class PaymentEntry(AccountsController):
|
||||
if d.reference_doctype not in valid_reference_doctypes:
|
||||
frappe.throw(
|
||||
_("Reference Doctype must be one of {0}").format(
|
||||
comma_or((_(d) for d in valid_reference_doctypes))
|
||||
comma_or(_(d) for d in valid_reference_doctypes)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -587,7 +591,8 @@ class PaymentEntry(AccountsController):
|
||||
if d.reference_doctype in frappe.get_hooks("invoice_doctypes"):
|
||||
if self.party_type == "Customer":
|
||||
ref_party_account = (
|
||||
get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
|
||||
get_party_account_based_on_invoice_discounting(d.reference_name)
|
||||
or ref_doc.debit_to
|
||||
)
|
||||
elif self.party_type == "Supplier":
|
||||
ref_party_account = ref_doc.credit_to
|
||||
@@ -600,7 +605,10 @@ class PaymentEntry(AccountsController):
|
||||
):
|
||||
frappe.throw(
|
||||
_("{0} {1} is associated with {2}, but Party Account is {3}").format(
|
||||
_(d.reference_doctype), d.reference_name, ref_party_account, self.party_account
|
||||
_(d.reference_doctype),
|
||||
d.reference_name,
|
||||
ref_party_account,
|
||||
self.party_account,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -611,7 +619,9 @@ class PaymentEntry(AccountsController):
|
||||
)
|
||||
|
||||
if ref_doc.docstatus != 1:
|
||||
frappe.throw(_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name))
|
||||
frappe.throw(
|
||||
_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name)
|
||||
)
|
||||
|
||||
def get_valid_reference_doctypes(self):
|
||||
if self.party_type == "Customer":
|
||||
@@ -782,9 +792,7 @@ class PaymentEntry(AccountsController):
|
||||
if not (is_single_currency and reference_is_multi_currency):
|
||||
return allocated_amount
|
||||
|
||||
allocated_amount = flt(
|
||||
allocated_amount / ref_exchange_rate, self.precision("total_allocated_amount")
|
||||
)
|
||||
allocated_amount = flt(allocated_amount / ref_exchange_rate, self.precision("total_allocated_amount"))
|
||||
|
||||
return allocated_amount
|
||||
|
||||
@@ -847,7 +855,6 @@ class PaymentEntry(AccountsController):
|
||||
accounts = []
|
||||
for d in self.taxes:
|
||||
if d.account_head == tax_withholding_details.get("account_head"):
|
||||
|
||||
# Preserve user updated included in paid amount
|
||||
if d.included_in_paid_amount:
|
||||
tax_withholding_details.update({"included_in_paid_amount": d.included_in_paid_amount})
|
||||
@@ -953,9 +960,9 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
def calculate_base_allocated_amount_for_reference(self, d) -> float:
|
||||
base_allocated_amount = 0
|
||||
advance_payment_doctypes = frappe.get_hooks(
|
||||
"advance_payment_receivable_doctypes"
|
||||
) + frappe.get_hooks("advance_payment_payable_doctypes")
|
||||
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
|
||||
"advance_payment_payable_doctypes"
|
||||
)
|
||||
if d.reference_doctype in advance_payment_doctypes:
|
||||
# When referencing Sales/Purchase Order, use the source/target exchange rate depending on payment type.
|
||||
# This is so there are no Exchange Gain/Loss generated for such doctypes
|
||||
@@ -970,7 +977,6 @@ class PaymentEntry(AccountsController):
|
||||
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
|
||||
)
|
||||
else:
|
||||
|
||||
# Use source/target exchange rate, so no difference amount is calculated.
|
||||
# then update exchange gain/loss amount in reference table
|
||||
# if there is an exchange gain/loss amount in reference table, submit a JE for that
|
||||
@@ -1108,7 +1114,6 @@ class PaymentEntry(AccountsController):
|
||||
)
|
||||
]
|
||||
else:
|
||||
|
||||
remarks = [
|
||||
_("Amount {0} {1} {2} {3}").format(
|
||||
_(self.party_account_currency),
|
||||
@@ -1128,14 +1133,19 @@ class PaymentEntry(AccountsController):
|
||||
if d.allocated_amount:
|
||||
remarks.append(
|
||||
_("Amount {0} {1} against {2} {3}").format(
|
||||
_(self.party_account_currency), d.allocated_amount, d.reference_doctype, d.reference_name
|
||||
_(self.party_account_currency),
|
||||
d.allocated_amount,
|
||||
d.reference_doctype,
|
||||
d.reference_name,
|
||||
)
|
||||
)
|
||||
|
||||
for d in self.get("deductions"):
|
||||
if d.amount:
|
||||
remarks.append(
|
||||
_("Amount {0} {1} deducted against {2}").format(_(self.company_currency), d.amount, d.account)
|
||||
_("Amount {0} {1} deducted against {2}").format(
|
||||
_(self.company_currency), d.amount, d.account
|
||||
)
|
||||
)
|
||||
|
||||
self.set("remarks", "\n".join(remarks))
|
||||
@@ -1209,21 +1219,31 @@ class PaymentEntry(AccountsController):
|
||||
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
|
||||
cost_center = self.cost_center
|
||||
if d.reference_doctype == "Sales Invoice" and not cost_center:
|
||||
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
|
||||
cost_center = frappe.db.get_value(
|
||||
d.reference_doctype, d.reference_name, "cost_center"
|
||||
)
|
||||
|
||||
gle = party_gl_dict.copy()
|
||||
|
||||
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
|
||||
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(
|
||||
d
|
||||
)
|
||||
reverse_dr_or_cr = 0
|
||||
|
||||
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return")
|
||||
payable_party_types = get_party_types_from_account_type("Payable")
|
||||
receivable_party_types = get_party_types_from_account_type("Receivable")
|
||||
if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"):
|
||||
if (
|
||||
is_return
|
||||
and self.party_type in receivable_party_types
|
||||
and (self.payment_type == "Pay")
|
||||
):
|
||||
reverse_dr_or_cr = 1
|
||||
elif (
|
||||
is_return and self.party_type in payable_party_types and (self.payment_type == "Receive")
|
||||
is_return
|
||||
and self.party_type in payable_party_types
|
||||
and (self.payment_type == "Receive")
|
||||
):
|
||||
reverse_dr_or_cr = 1
|
||||
|
||||
@@ -1306,9 +1326,7 @@ class PaymentEntry(AccountsController):
|
||||
if getdate(posting_date) < getdate(self.posting_date):
|
||||
posting_date = self.posting_date
|
||||
|
||||
dr_or_cr = (
|
||||
"credit" if invoice.reference_doctype in ["Sales Invoice", "Payment Entry"] else "debit"
|
||||
)
|
||||
dr_or_cr = "credit" if invoice.reference_doctype in ["Sales Invoice", "Payment Entry"] else "debit"
|
||||
args_dict["account"] = invoice.account
|
||||
args_dict[dr_or_cr] = invoice.allocated_amount
|
||||
args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount
|
||||
@@ -1694,7 +1712,8 @@ def get_outstanding_reference_documents(args, validate=False):
|
||||
return []
|
||||
elif supplier_status["hold_type"] == "Payments":
|
||||
if (
|
||||
not supplier_status["release_date"] or getdate(nowdate()) <= supplier_status["release_date"]
|
||||
not supplier_status["release_date"]
|
||||
or getdate(nowdate()) <= supplier_status["release_date"]
|
||||
):
|
||||
return []
|
||||
|
||||
@@ -1704,7 +1723,7 @@ def get_outstanding_reference_documents(args, validate=False):
|
||||
# Get positive outstanding sales /purchase invoices
|
||||
condition = ""
|
||||
if args.get("voucher_type") and args.get("voucher_no"):
|
||||
condition = " and voucher_type={0} and voucher_no={1}".format(
|
||||
condition = " and voucher_type={} and voucher_no={}".format(
|
||||
frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])
|
||||
)
|
||||
common_filter.append(ple.voucher_type == args["voucher_type"])
|
||||
@@ -1719,7 +1738,7 @@ def get_outstanding_reference_documents(args, validate=False):
|
||||
active_dimensions = get_dimensions()[0]
|
||||
for dim in active_dimensions:
|
||||
if args.get(dim.fieldname):
|
||||
condition += " and {0}='{1}'".format(dim.fieldname, args.get(dim.fieldname))
|
||||
condition += f" and {dim.fieldname}='{args.get(dim.fieldname)}'"
|
||||
accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname))
|
||||
|
||||
date_fields_dict = {
|
||||
@@ -1729,21 +1748,21 @@ def get_outstanding_reference_documents(args, validate=False):
|
||||
|
||||
for fieldname, date_fields in date_fields_dict.items():
|
||||
if args.get(date_fields[0]) and args.get(date_fields[1]):
|
||||
condition += " and {0} between '{1}' and '{2}'".format(
|
||||
condition += " and {} between '{}' and '{}'".format(
|
||||
fieldname, args.get(date_fields[0]), args.get(date_fields[1])
|
||||
)
|
||||
posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
|
||||
elif args.get(date_fields[0]):
|
||||
# if only from date is supplied
|
||||
condition += " and {0} >= '{1}'".format(fieldname, args.get(date_fields[0]))
|
||||
condition += f" and {fieldname} >= '{args.get(date_fields[0])}'"
|
||||
posting_and_due_date.append(ple[fieldname].gte(args.get(date_fields[0])))
|
||||
elif args.get(date_fields[1]):
|
||||
# if only to date is supplied
|
||||
condition += " and {0} <= '{1}'".format(fieldname, args.get(date_fields[1]))
|
||||
condition += f" and {fieldname} <= '{args.get(date_fields[1])}'"
|
||||
posting_and_due_date.append(ple[fieldname].lte(args.get(date_fields[1])))
|
||||
|
||||
if args.get("company"):
|
||||
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
||||
condition += " and company = {}".format(frappe.db.escape(args.get("company")))
|
||||
common_filter.append(ple.company == args.get("company"))
|
||||
|
||||
outstanding_invoices = []
|
||||
@@ -1859,12 +1878,10 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list
|
||||
return outstanding_invoices_after_split
|
||||
|
||||
|
||||
def get_currency_data(outstanding_invoices: list, company: str = None) -> dict:
|
||||
def get_currency_data(outstanding_invoices: list, company: str | None = None) -> dict:
|
||||
"""Get currency and conversion data for a list of invoices."""
|
||||
exc_rates = frappe._dict()
|
||||
company_currency = (
|
||||
frappe.db.get_value("Company", company, "default_currency") if company else None
|
||||
)
|
||||
company_currency = frappe.db.get_value("Company", company, "default_currency") if company else None
|
||||
|
||||
for doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
|
||||
@@ -1959,7 +1976,7 @@ def get_orders_to_be_billed(
|
||||
active_dimensions = get_dimensions()[0]
|
||||
for dim in active_dimensions:
|
||||
if filters.get(dim.fieldname):
|
||||
condition += " and {0}='{1}'".format(dim.fieldname, filters.get(dim.fieldname))
|
||||
condition += f" and {dim.fieldname}='{filters.get(dim.fieldname)}'"
|
||||
|
||||
if party_account_currency == company_currency:
|
||||
grand_total_field = "base_grand_total"
|
||||
@@ -2109,18 +2126,14 @@ def get_account_details(account, date, cost_center=None):
|
||||
frappe.has_permission("Payment Entry", throw=True)
|
||||
|
||||
# to check if the passed account is accessible under reference doctype Payment Entry
|
||||
account_list = frappe.get_list(
|
||||
"Account", {"name": account}, reference_doctype="Payment Entry", limit=1
|
||||
)
|
||||
account_list = frappe.get_list("Account", {"name": account}, reference_doctype="Payment Entry", limit=1)
|
||||
|
||||
# There might be some user permissions which will allow account under certain doctypes
|
||||
# except for Payment Entry, only in such case we should throw permission error
|
||||
if not account_list:
|
||||
frappe.throw(_("Account: {0} is not permitted under Payment Entry").format(account))
|
||||
|
||||
account_balance = get_balance_on(
|
||||
account, date, cost_center=cost_center, ignore_account_permission=True
|
||||
)
|
||||
account_balance = get_balance_on(account, date, cost_center=cost_center, ignore_account_permission=True)
|
||||
|
||||
return frappe._dict(
|
||||
{
|
||||
@@ -2167,9 +2180,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
total_amount = outstanding_amount = exchange_rate = account = None
|
||||
|
||||
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
||||
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(
|
||||
ref_doc.company
|
||||
)
|
||||
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company)
|
||||
|
||||
if reference_doctype == "Dunning":
|
||||
total_amount = outstanding_amount = ref_doc.get("dunning_amount")
|
||||
@@ -2178,9 +2189,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
|
||||
total_amount = ref_doc.get("total_amount")
|
||||
if ref_doc.multi_currency:
|
||||
exchange_rate = get_exchange_rate(
|
||||
party_account_currency, company_currency, ref_doc.posting_date
|
||||
)
|
||||
exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
||||
else:
|
||||
exchange_rate = 1
|
||||
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
||||
@@ -2250,9 +2259,7 @@ def get_payment_entry(
|
||||
):
|
||||
doc = frappe.get_doc(dt, dn)
|
||||
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
|
||||
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
|
||||
100.0 + over_billing_allowance
|
||||
):
|
||||
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (100.0 + over_billing_allowance):
|
||||
frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt)))
|
||||
|
||||
if not party_type:
|
||||
@@ -2305,9 +2312,7 @@ def get_payment_entry(
|
||||
pe.paid_from_account_currency = (
|
||||
party_account_currency if payment_type == "Receive" else bank.account_currency
|
||||
)
|
||||
pe.paid_to_account_currency = (
|
||||
party_account_currency if payment_type == "Pay" else bank.account_currency
|
||||
)
|
||||
pe.paid_to_account_currency = party_account_currency if payment_type == "Pay" else bank.account_currency
|
||||
pe.paid_amount = paid_amount
|
||||
pe.received_amount = received_amount
|
||||
pe.letter_head = doc.get("letter_head")
|
||||
@@ -2336,7 +2341,6 @@ def get_payment_entry(
|
||||
doc.payment_terms_template,
|
||||
"allocate_payment_based_on_payment_terms",
|
||||
):
|
||||
|
||||
for reference in get_reference_as_per_payment_terms(
|
||||
doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount, party_account_currency
|
||||
):
|
||||
@@ -2524,9 +2528,7 @@ def set_paid_amount_and_received_amount(
|
||||
return paid_amount, received_amount
|
||||
|
||||
|
||||
def apply_early_payment_discount(
|
||||
paid_amount, received_amount, doc, party_account_currency, reference_date
|
||||
):
|
||||
def apply_early_payment_discount(paid_amount, received_amount, doc, party_account_currency, reference_date):
|
||||
total_discount = 0
|
||||
valid_discounts = []
|
||||
eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"]
|
||||
@@ -2536,7 +2538,6 @@ def apply_early_payment_discount(
|
||||
if doc.doctype in eligible_for_payments and has_payment_schedule:
|
||||
for term in doc.payment_schedule:
|
||||
if not term.discounted_amount and term.discount and reference_date <= term.discount_date:
|
||||
|
||||
if term.discount_type == "Percentage":
|
||||
grand_total = doc.get("grand_total") if is_multi_currency else doc.get("base_grand_total")
|
||||
discount_amount = flt(grand_total) * (term.discount / 100)
|
||||
@@ -2565,9 +2566,7 @@ def apply_early_payment_discount(
|
||||
return paid_amount, received_amount, total_discount, valid_discounts
|
||||
|
||||
|
||||
def set_pending_discount_loss(
|
||||
pe, doc, discount_amount, base_total_discount_loss, party_account_currency
|
||||
):
|
||||
def set_pending_discount_loss(pe, doc, discount_amount, base_total_discount_loss, party_account_currency):
|
||||
# If multi-currency, get base discount amount to adjust with base currency deductions/losses
|
||||
if party_account_currency != doc.company_currency:
|
||||
discount_amount = discount_amount * doc.get("conversion_rate", 1)
|
||||
@@ -2587,7 +2586,8 @@ def set_pending_discount_loss(
|
||||
pe.set_gain_or_loss(
|
||||
account_details={
|
||||
"account": frappe.get_cached_value("Company", pe.company, account_type),
|
||||
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"),
|
||||
"cost_center": pe.cost_center
|
||||
or frappe.get_cached_value("Company", pe.company, "cost_center"),
|
||||
"amount": discount_amount * positive_negative,
|
||||
}
|
||||
)
|
||||
@@ -2610,9 +2610,7 @@ def split_early_payment_discount_loss(pe, doc, valid_discounts) -> float:
|
||||
def get_total_discount_percent(doc, valid_discounts) -> float:
|
||||
"""Get total percentage and amount discount applied as a percentage."""
|
||||
total_discount_percent = (
|
||||
sum(
|
||||
discount.get("discount") for discount in valid_discounts if discount.get("type") == "Percentage"
|
||||
)
|
||||
sum(discount.get("discount") for discount in valid_discounts if discount.get("type") == "Percentage")
|
||||
or 0.0
|
||||
)
|
||||
|
||||
@@ -2655,9 +2653,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
|
||||
|
||||
# The same account head could be used more than once
|
||||
for tax in doc.get("taxes", []):
|
||||
base_tax_loss = tax.get("base_tax_amount_after_discount_amount") * (
|
||||
total_discount_percentage / 100
|
||||
)
|
||||
base_tax_loss = tax.get("base_tax_amount_after_discount_amount") * (total_discount_percentage / 100)
|
||||
|
||||
account = tax.get("account_head")
|
||||
if not tax_discount_loss.get(account):
|
||||
@@ -2674,7 +2670,8 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
|
||||
"deductions",
|
||||
{
|
||||
"account": account,
|
||||
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"),
|
||||
"cost_center": pe.cost_center
|
||||
or frappe.get_cached_value("Company", pe.company, "cost_center"),
|
||||
"amount": flt(loss, precision),
|
||||
},
|
||||
)
|
||||
@@ -2697,7 +2694,8 @@ def get_reference_as_per_payment_terms(
|
||||
if not is_multi_currency_acc:
|
||||
# If accounting is done in company currency for multi-currency transaction
|
||||
payment_term_outstanding = flt(
|
||||
payment_term_outstanding * doc.get("conversion_rate"), payment_term.precision("payment_amount")
|
||||
payment_term_outstanding * doc.get("conversion_rate"),
|
||||
payment_term.precision("payment_amount"),
|
||||
)
|
||||
|
||||
if payment_term_outstanding:
|
||||
@@ -2725,7 +2723,7 @@ def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
||||
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
||||
|
||||
paid_amount = frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select ifnull(sum({dr_or_cr}), 0) as paid_amount
|
||||
from `tabGL Entry`
|
||||
where against_voucher_type = %s
|
||||
@@ -2735,9 +2733,7 @@ def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
||||
and account = %s
|
||||
and due_date = %s
|
||||
and {dr_or_cr} > 0
|
||||
""".format(
|
||||
dr_or_cr=dr_or_cr
|
||||
),
|
||||
""",
|
||||
(dt, dn, party_type, party, account, due_date),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
@@ -10,7 +9,6 @@ from frappe.utils import add_days, flt, nowdate
|
||||
|
||||
from erpnext.accounts.doctype.account.test_account import create_account
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import (
|
||||
InvalidPaymentEntry,
|
||||
get_outstanding_reference_documents,
|
||||
get_payment_entry,
|
||||
get_reference_details,
|
||||
@@ -162,7 +160,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
|
||||
supplier.on_hold = 0
|
||||
supplier.save()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
raise Exception
|
||||
@@ -469,9 +467,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
pe = get_payment_entry(
|
||||
"Sales Invoice", si.name, bank_account="_Test Bank - _TC", bank_amount=4700
|
||||
)
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC", bank_amount=4700)
|
||||
pe.reference_no = si.name
|
||||
pe.reference_date = nowdate()
|
||||
|
||||
@@ -638,9 +634,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
pe.set_exchange_rate()
|
||||
pe.set_amounts()
|
||||
|
||||
self.assertEqual(
|
||||
pe.source_exchange_rate, 65.1, "{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
|
||||
)
|
||||
self.assertEqual(pe.source_exchange_rate, 65.1, f"{pe.source_exchange_rate} is not equal to {65.1}")
|
||||
|
||||
def test_internal_transfer_usd_to_inr(self):
|
||||
pe = frappe.new_doc("Payment Entry")
|
||||
@@ -896,9 +890,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
|
||||
pi = make_purchase_invoice_against_cost_center(
|
||||
cost_center=cost_center, credit_to="Creditors - _TC"
|
||||
)
|
||||
pi = make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC")
|
||||
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
self.assertEqual(pe.cost_center, pi.cost_center)
|
||||
@@ -939,9 +931,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
|
||||
|
||||
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=si.cost_center)
|
||||
party_balance = get_balance_on(
|
||||
party_type="Customer", party=si.customer, cost_center=si.cost_center
|
||||
)
|
||||
party_balance = get_balance_on(party_type="Customer", party=si.customer, cost_center=si.cost_center)
|
||||
party_account_balance = get_balance_on(si.debit_to, cost_center=si.cost_center)
|
||||
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||
@@ -1203,7 +1193,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
Overallocation validation shouldn't fire for Template without "Allocate Payment based on Payment Terms" enabled
|
||||
|
||||
"""
|
||||
customer = create_customer()
|
||||
create_customer()
|
||||
create_payment_terms_template()
|
||||
|
||||
template = frappe.get_doc("Payment Terms Template", "Test Receivable Template")
|
||||
@@ -1262,9 +1252,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
create_payment_terms_template()
|
||||
|
||||
# SI has an earlier due date and SI2 has a later due date
|
||||
si = create_sales_invoice(
|
||||
qty=1, rate=100, customer=customer, posting_date=add_days(nowdate(), -4)
|
||||
)
|
||||
si = create_sales_invoice(qty=1, rate=100, customer=customer, posting_date=add_days(nowdate(), -4))
|
||||
si2 = create_sales_invoice(do_not_save=1, qty=1, rate=100, customer=customer)
|
||||
si2.payment_terms_template = "Test Receivable Template"
|
||||
si2.submit()
|
||||
@@ -1363,8 +1351,6 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
self.check_gl_entries()
|
||||
|
||||
def test_ledger_entries_for_advance_as_liability(self):
|
||||
from erpnext.accounts.doctype.account.test_account import create_account
|
||||
|
||||
company = "_Test Company"
|
||||
|
||||
advance_account = create_account(
|
||||
@@ -1466,7 +1452,7 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
self.check_pl_entries()
|
||||
|
||||
# Unreconcile
|
||||
unrecon = (
|
||||
(
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Unreconcile Payment",
|
||||
@@ -1554,8 +1540,6 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
self.assertEqual(len(pr.payments), 0)
|
||||
|
||||
def test_advance_reverse_payment_reconciliation(self):
|
||||
from erpnext.accounts.doctype.account.test_account import create_account
|
||||
|
||||
company = "_Test Company"
|
||||
customer = create_customer(frappe.generate_hash(length=10), "INR")
|
||||
advance_account = create_account(
|
||||
@@ -1645,14 +1629,16 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
self.check_pl_entries()
|
||||
|
||||
# Unreconcile
|
||||
unrecon = (
|
||||
(
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Unreconcile Payment",
|
||||
"company": company,
|
||||
"voucher_type": pe.doctype,
|
||||
"voucher_no": pe.name,
|
||||
"allocations": [{"reference_doctype": reverse_pe.doctype, "reference_name": reverse_pe.name}],
|
||||
"allocations": [
|
||||
{"reference_doctype": reverse_pe.doctype, "reference_name": reverse_pe.name}
|
||||
],
|
||||
}
|
||||
)
|
||||
.save()
|
||||
@@ -1703,12 +1689,11 @@ def create_payment_entry(**args):
|
||||
|
||||
|
||||
def create_payment_terms_template():
|
||||
|
||||
create_payment_term("Basic Amount Receivable")
|
||||
create_payment_term("Tax Receivable")
|
||||
|
||||
if not frappe.db.exists("Payment Terms Template", "Test Receivable Template"):
|
||||
payment_term_template = frappe.get_doc(
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Payment Terms Template",
|
||||
"template_name": "Test Receivable Template",
|
||||
|
||||
@@ -137,9 +137,9 @@ class PaymentLedgerEntry(Document):
|
||||
):
|
||||
if not self.get(dimension.fieldname):
|
||||
frappe.throw(
|
||||
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
|
||||
dimension.label, self.account
|
||||
)
|
||||
_(
|
||||
"Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}."
|
||||
).format(dimension.label, self.account)
|
||||
)
|
||||
|
||||
if (
|
||||
@@ -150,9 +150,9 @@ class PaymentLedgerEntry(Document):
|
||||
):
|
||||
if not self.get(dimension.fieldname):
|
||||
frappe.throw(
|
||||
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
|
||||
dimension.label, self.account
|
||||
)
|
||||
_(
|
||||
"Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}."
|
||||
).format(dimension.label, self.account)
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
|
||||
@@ -84,11 +84,14 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
self.customer = customer.name
|
||||
|
||||
def create_sales_invoice(
|
||||
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||
self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
|
||||
):
|
||||
"""
|
||||
Helper function to populate default values in sales invoice
|
||||
"""
|
||||
if posting_date is None:
|
||||
posting_date = nowdate()
|
||||
|
||||
sinv = create_sales_invoice(
|
||||
qty=qty,
|
||||
rate=rate,
|
||||
@@ -112,10 +115,12 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
)
|
||||
return sinv
|
||||
|
||||
def create_payment_entry(self, amount=100, posting_date=nowdate()):
|
||||
def create_payment_entry(self, amount=100, posting_date=None):
|
||||
"""
|
||||
Helper function to populate default values in payment entry
|
||||
"""
|
||||
if posting_date is None:
|
||||
posting_date = nowdate()
|
||||
payment = create_payment_entry(
|
||||
company=self.company,
|
||||
payment_type="Receive",
|
||||
@@ -128,9 +133,10 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
payment.posting_date = posting_date
|
||||
return payment
|
||||
|
||||
def create_sales_order(
|
||||
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||
):
|
||||
def create_sales_order(self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False):
|
||||
if posting_date is None:
|
||||
posting_date = nowdate()
|
||||
|
||||
so = make_sales_order(
|
||||
company=self.company,
|
||||
transaction_date=posting_date,
|
||||
@@ -159,9 +165,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
for doctype in doctype_list:
|
||||
qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
|
||||
|
||||
def create_journal_entry(
|
||||
self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None
|
||||
):
|
||||
def create_journal_entry(self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None):
|
||||
je = frappe.new_doc("Journal Entry")
|
||||
je.posting_date = posting_date or nowdate()
|
||||
je.company = self.company
|
||||
@@ -319,9 +323,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
ple.amount,
|
||||
ple.delinked,
|
||||
)
|
||||
.where(
|
||||
(ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name)
|
||||
)
|
||||
.where((ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name))
|
||||
.orderby(ple.creation)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
@@ -362,9 +364,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
)
|
||||
cr_note2.is_return = 1
|
||||
cr_note2 = cr_note2.save().submit()
|
||||
je1 = self.create_journal_entry(
|
||||
self.debit_to, self.debit_to, amount, posting_date=transaction_date
|
||||
)
|
||||
je1 = self.create_journal_entry(self.debit_to, self.debit_to, amount, posting_date=transaction_date)
|
||||
je1.get("accounts")[0].party_type = je1.get("accounts")[1].party_type = "Customer"
|
||||
je1.get("accounts")[0].party = je1.get("accounts")[1].party = self.customer
|
||||
je1.get("accounts")[0].reference_type = cr_note2.doctype
|
||||
@@ -419,9 +419,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
ple.amount,
|
||||
ple.delinked,
|
||||
)
|
||||
.where(
|
||||
(ple.against_voucher_type == cr_note2.doctype) & (ple.against_voucher_no == cr_note2.name)
|
||||
)
|
||||
.where((ple.against_voucher_type == cr_note2.doctype) & (ple.against_voucher_no == cr_note2.name))
|
||||
.orderby(ple.creation)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
@@ -518,7 +516,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
|
||||
amount = 100
|
||||
so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit()
|
||||
|
||||
pe = get_payment_entry(so.doctype, so.name).save().submit()
|
||||
get_payment_entry(so.doctype, so.name).save().submit()
|
||||
|
||||
so.reload()
|
||||
so.cancel()
|
||||
|
||||
@@ -90,9 +90,7 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
|
||||
je = frappe.new_doc("Journal Entry")
|
||||
je.payment_order = doc.name
|
||||
je.posting_date = nowdate()
|
||||
mode_of_payment_type = frappe._dict(
|
||||
frappe.get_all("Mode of Payment", fields=["name", "type"], as_list=1)
|
||||
)
|
||||
mode_of_payment_type = frappe._dict(frappe.get_all("Mode of Payment", fields=["name", "type"], as_list=1))
|
||||
|
||||
je.voucher_type = "Bank Entry"
|
||||
if mode_of_payment and mode_of_payment_type.get(mode_of_payment) == "Cash":
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
@@ -41,9 +40,7 @@ class TestPaymentOrder(FrappeTestCase):
|
||||
payment_entry.insert()
|
||||
payment_entry.submit()
|
||||
|
||||
doc = create_payment_order_against_payment_entry(
|
||||
payment_entry, "Payment Entry", self.bank_account
|
||||
)
|
||||
doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry", self.bank_account)
|
||||
reference_doc = doc.get("references")[0]
|
||||
self.assertEqual(reference_doc.reference_name, payment_entry.name)
|
||||
self.assertEqual(reference_doc.reference_doctype, "Payment Entry")
|
||||
|
||||
@@ -67,7 +67,7 @@ class PaymentReconciliation(Document):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PaymentReconciliation, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.common_filter_conditions = []
|
||||
self.accounting_dimension_filter_conditions = []
|
||||
self.ple_posting_date_filter = []
|
||||
@@ -286,7 +286,6 @@ class PaymentReconciliation(Document):
|
||||
self.return_invoices = self.return_invoices_query.run(as_dict=True)
|
||||
|
||||
def get_dr_or_cr_notes(self):
|
||||
|
||||
self.build_qb_filter_conditions(get_return_invoices=True)
|
||||
|
||||
ple = qb.DocType("Payment Ledger Entry")
|
||||
@@ -412,9 +411,7 @@ class PaymentReconciliation(Document):
|
||||
payment_entry[0].get("reference_name")
|
||||
)
|
||||
|
||||
new_difference_amount = self.get_difference_amount(
|
||||
payment_entry[0], invoice[0], allocated_amount
|
||||
)
|
||||
new_difference_amount = self.get_difference_amount(payment_entry[0], invoice[0], allocated_amount)
|
||||
return new_difference_amount
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -532,9 +529,9 @@ class PaymentReconciliation(Document):
|
||||
|
||||
if running_doc:
|
||||
frappe.throw(
|
||||
_("A Reconciliation Job {0} is running for the same filters. Cannot reconcile now").format(
|
||||
get_link_to_form("Auto Reconcile", running_doc)
|
||||
)
|
||||
_(
|
||||
"A Reconciliation Job {0} is running for the same filters. Cannot reconcile now"
|
||||
).format(get_link_to_form("Auto Reconcile", running_doc))
|
||||
)
|
||||
return
|
||||
|
||||
@@ -627,9 +624,7 @@ class PaymentReconciliation(Document):
|
||||
|
||||
invoice_exchange_map.update(purchase_invoice_map)
|
||||
|
||||
journals = [
|
||||
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Journal Entry"
|
||||
]
|
||||
journals = [d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Journal Entry"]
|
||||
journals.extend(
|
||||
[d.get("reference_name") for d in payments if d.get("reference_type") == "Journal Entry"]
|
||||
)
|
||||
@@ -721,7 +716,7 @@ class PaymentReconciliation(Document):
|
||||
def get_journal_filter_conditions(self):
|
||||
conditions = []
|
||||
je = qb.DocType("Journal Entry")
|
||||
jea = qb.DocType("Journal Entry Account")
|
||||
qb.DocType("Journal Entry Account")
|
||||
conditions.append(je.company == self.company)
|
||||
|
||||
if self.from_payment_date:
|
||||
@@ -841,7 +836,7 @@ def adjust_allocations_for_taxes(doc):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_queries_for_dimension_filters(company: str = None):
|
||||
def get_queries_for_dimension_filters(company: str | None = None):
|
||||
dimensions_with_filters = []
|
||||
for d in get_dimensions()[0]:
|
||||
filters = {}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import qb
|
||||
@@ -127,11 +126,14 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
setattr(self, x.attribute, acc.name)
|
||||
|
||||
def create_sales_invoice(
|
||||
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||
self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
|
||||
):
|
||||
"""
|
||||
Helper function to populate default values in sales invoice
|
||||
"""
|
||||
if posting_date is None:
|
||||
posting_date = nowdate()
|
||||
|
||||
sinv = create_sales_invoice(
|
||||
qty=qty,
|
||||
rate=rate,
|
||||
@@ -155,10 +157,13 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
)
|
||||
return sinv
|
||||
|
||||
def create_payment_entry(self, amount=100, posting_date=nowdate(), customer=None):
|
||||
def create_payment_entry(self, amount=100, posting_date=None, customer=None):
|
||||
"""
|
||||
Helper function to populate default values in payment entry
|
||||
"""
|
||||
if posting_date is None:
|
||||
posting_date = nowdate()
|
||||
|
||||
payment = create_payment_entry(
|
||||
company=self.company,
|
||||
payment_type="Receive",
|
||||
@@ -172,11 +177,14 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
return payment
|
||||
|
||||
def create_purchase_invoice(
|
||||
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||
self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
|
||||
):
|
||||
"""
|
||||
Helper function to populate default values in sales invoice
|
||||
"""
|
||||
if posting_date is None:
|
||||
posting_date = nowdate()
|
||||
|
||||
pinv = make_purchase_invoice(
|
||||
qty=qty,
|
||||
rate=rate,
|
||||
@@ -201,11 +209,14 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
return pinv
|
||||
|
||||
def create_purchase_order(
|
||||
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||
self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
|
||||
):
|
||||
"""
|
||||
Helper function to populate default values in sales invoice
|
||||
"""
|
||||
if posting_date is None:
|
||||
posting_date = nowdate()
|
||||
|
||||
pord = create_purchase_order(
|
||||
qty=qty,
|
||||
rate=rate,
|
||||
@@ -250,9 +261,7 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
|
||||
return pr
|
||||
|
||||
def create_journal_entry(
|
||||
self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None
|
||||
):
|
||||
def create_journal_entry(self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None):
|
||||
je = frappe.new_doc("Journal Entry")
|
||||
je.posting_date = posting_date or nowdate()
|
||||
je.company = self.company
|
||||
@@ -402,7 +411,7 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
rate = 100
|
||||
invoices = []
|
||||
payments = []
|
||||
for i in range(5):
|
||||
for _i in range(5):
|
||||
invoices.append(self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date))
|
||||
pe = self.create_payment_entry(amount=rate, posting_date=transaction_date).save().submit()
|
||||
payments.append(pe)
|
||||
@@ -821,9 +830,7 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
|
||||
cr_note.cancel()
|
||||
|
||||
pay = self.create_payment_entry(
|
||||
amount=amount, posting_date=transaction_date, customer=self.customer3
|
||||
)
|
||||
pay = self.create_payment_entry(amount=amount, posting_date=transaction_date, customer=self.customer3)
|
||||
pay.paid_from = self.debtors_eur
|
||||
pay.paid_from_account_currency = "EUR"
|
||||
pay.source_exchange_rate = exchange_rate
|
||||
@@ -1025,9 +1032,7 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
rate = 100
|
||||
|
||||
# 'Main - PR' Cost Center
|
||||
si1 = self.create_sales_invoice(
|
||||
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
|
||||
)
|
||||
si1 = self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True)
|
||||
si1.cost_center = self.main_cc.name
|
||||
si1.submit()
|
||||
|
||||
@@ -1043,9 +1048,7 @@ class TestPaymentReconciliation(FrappeTestCase):
|
||||
je1 = je1.save().submit()
|
||||
|
||||
# 'Sub - PR' Cost Center
|
||||
si2 = self.create_sales_invoice(
|
||||
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
|
||||
)
|
||||
si2 = self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True)
|
||||
si2.cost_center = self.sub_cc.name
|
||||
si2.submit()
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ class PaymentRequest(Document):
|
||||
)
|
||||
|
||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||
if not hasattr(ref_doc, "order_type") or getattr(ref_doc, "order_type") != "Shopping Cart":
|
||||
if not hasattr(ref_doc, "order_type") or ref_doc.order_type != "Shopping Cart":
|
||||
ref_amount = get_amount(ref_doc, self.payment_account)
|
||||
if not ref_amount:
|
||||
frappe.throw(_("Payment Entry is already created"))
|
||||
@@ -160,7 +160,7 @@ class PaymentRequest(Document):
|
||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||
|
||||
if (
|
||||
hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"
|
||||
hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart"
|
||||
) or self.flags.mute_email:
|
||||
send_mail = False
|
||||
|
||||
@@ -172,9 +172,9 @@ class PaymentRequest(Document):
|
||||
elif self.payment_channel == "Phone":
|
||||
self.request_phone_payment()
|
||||
|
||||
advance_payment_doctypes = frappe.get_hooks(
|
||||
"advance_payment_receivable_doctypes"
|
||||
) + frappe.get_hooks("advance_payment_payable_doctypes")
|
||||
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
|
||||
"advance_payment_payable_doctypes"
|
||||
)
|
||||
if self.reference_doctype in advance_payment_doctypes:
|
||||
# set advance payment status
|
||||
ref_doc.set_total_advance_paid()
|
||||
@@ -218,16 +218,16 @@ class PaymentRequest(Document):
|
||||
self.set_as_cancelled()
|
||||
|
||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||
advance_payment_doctypes = frappe.get_hooks(
|
||||
"advance_payment_receivable_doctypes"
|
||||
) + frappe.get_hooks("advance_payment_payable_doctypes")
|
||||
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
|
||||
"advance_payment_payable_doctypes"
|
||||
)
|
||||
if self.reference_doctype in advance_payment_doctypes:
|
||||
# set advance payment status
|
||||
ref_doc.set_total_advance_paid()
|
||||
|
||||
def make_invoice(self):
|
||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
|
||||
if hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart":
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
|
||||
|
||||
si = make_sales_invoice(self.reference_name, ignore_permissions=True)
|
||||
@@ -306,14 +306,10 @@ class PaymentRequest(Document):
|
||||
else:
|
||||
party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
|
||||
|
||||
party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(
|
||||
party_account
|
||||
)
|
||||
party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account)
|
||||
|
||||
bank_amount = self.grand_total
|
||||
if (
|
||||
party_account_currency == ref_doc.company_currency and party_account_currency != self.currency
|
||||
):
|
||||
if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
|
||||
party_amount = ref_doc.get("base_rounded_total") or ref_doc.get("base_grand_total")
|
||||
else:
|
||||
party_amount = self.grand_total
|
||||
@@ -331,7 +327,7 @@ class PaymentRequest(Document):
|
||||
"mode_of_payment": self.mode_of_payment,
|
||||
"reference_no": self.name,
|
||||
"reference_date": nowdate(),
|
||||
"remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(
|
||||
"remarks": "Payment Entry against {} {} via Payment Request {}".format(
|
||||
self.reference_doctype, self.reference_name, self.name
|
||||
),
|
||||
}
|
||||
@@ -465,15 +461,11 @@ def make_payment_request(**args):
|
||||
frappe.db.set_value(
|
||||
"Sales Order", args.dn, "loyalty_points", int(args.loyalty_points), update_modified=False
|
||||
)
|
||||
frappe.db.set_value(
|
||||
"Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False
|
||||
)
|
||||
frappe.db.set_value("Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False)
|
||||
grand_total = grand_total - loyalty_amount
|
||||
|
||||
bank_account = (
|
||||
get_party_bank_account(args.get("party_type"), args.get("party"))
|
||||
if args.get("party_type")
|
||||
else ""
|
||||
get_party_bank_account(args.get("party_type"), args.get("party")) if args.get("party_type") else ""
|
||||
)
|
||||
|
||||
draft_payment_request = frappe.db.get_value(
|
||||
@@ -770,7 +762,10 @@ def get_paid_amount_against_order(dt, dn):
|
||||
& (
|
||||
(pe_ref.reference_name == dn)
|
||||
| pe_ref.reference_name.isin(
|
||||
frappe.qb.from_(inv_item).select(inv_item.parent).where(inv_item[inv_field] == dn).distinct()
|
||||
frappe.qb.from_(inv_item)
|
||||
.select(inv_item.parent)
|
||||
.where(inv_item[inv_field] == dn)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -93,7 +93,7 @@ class TestPaymentRequest(unittest.TestCase):
|
||||
return_doc=1,
|
||||
)
|
||||
|
||||
pe = pr.create_payment_entry()
|
||||
pr.create_payment_entry()
|
||||
pr.load_from_db()
|
||||
|
||||
self.assertEqual(pr.status, "Paid")
|
||||
@@ -158,7 +158,7 @@ class TestPaymentRequest(unittest.TestCase):
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_gle[gle.account][0], gle.account)
|
||||
self.assertEqual(expected_gle[gle.account][1], gle.debit)
|
||||
self.assertEqual(expected_gle[gle.account][2], gle.credit)
|
||||
|
||||
@@ -36,9 +36,7 @@ class PaymentTermsTemplate(Document):
|
||||
total_portion += flt(term.get("invoice_portion", 0))
|
||||
|
||||
if flt(total_portion, 2) != 100.00:
|
||||
frappe.msgprint(
|
||||
_("Combined invoice portion must equal 100%"), raise_exception=1, indicator="red"
|
||||
)
|
||||
frappe.msgprint(_("Combined invoice portion must equal 100%"), raise_exception=1, indicator="red")
|
||||
|
||||
def validate_terms(self):
|
||||
terms = []
|
||||
|
||||
@@ -67,7 +67,8 @@ class PeriodClosingVoucher(AccountsController):
|
||||
enqueue_after_commit=True,
|
||||
)
|
||||
frappe.msgprint(
|
||||
_("The GL Entries will be cancelled in the background, it can take a few minutes."), alert=True
|
||||
_("The GL Entries will be cancelled in the background, it can take a few minutes."),
|
||||
alert=True,
|
||||
)
|
||||
else:
|
||||
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
|
||||
@@ -109,9 +110,7 @@ class PeriodClosingVoucher(AccountsController):
|
||||
self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self
|
||||
)
|
||||
|
||||
self.year_start_date = get_fiscal_year(
|
||||
self.posting_date, self.fiscal_year, company=self.company
|
||||
)[1]
|
||||
self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year, company=self.company)[1]
|
||||
|
||||
self.check_if_previous_year_closed()
|
||||
|
||||
@@ -225,7 +224,9 @@ class PeriodClosingVoucher(AccountsController):
|
||||
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
|
||||
if flt(acc.bal_in_account_currency) > 0
|
||||
else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency))
|
||||
if flt(acc.bal_in_company_currency) > 0
|
||||
else 0,
|
||||
"is_period_closing_voucher_entry": 1,
|
||||
},
|
||||
item=acc,
|
||||
@@ -249,7 +250,9 @@ class PeriodClosingVoucher(AccountsController):
|
||||
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
|
||||
if flt(acc.bal_in_account_currency) < 0
|
||||
else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency))
|
||||
if flt(acc.bal_in_company_currency) < 0
|
||||
else 0,
|
||||
"is_period_closing_voucher_entry": 1,
|
||||
},
|
||||
item=acc,
|
||||
|
||||
@@ -70,7 +70,7 @@ class POSClosingEntry(StatusUpdater):
|
||||
for key, value in pos_occurences.items():
|
||||
if len(value) > 1:
|
||||
error_list.append(
|
||||
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
|
||||
_(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}")
|
||||
)
|
||||
|
||||
if error_list:
|
||||
@@ -165,9 +165,7 @@ def get_pos_invoices(start, end, pos_profile, user):
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
data = list(
|
||||
filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data)
|
||||
)
|
||||
data = list(filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data))
|
||||
# need to get taxes and payments so can't avoid get_doc
|
||||
data = [frappe.get_doc("POS Invoice", d.name).as_dict() for d in data]
|
||||
|
||||
@@ -238,7 +236,11 @@ def make_closing_entry_from_opening(opening_entry):
|
||||
else:
|
||||
payments.append(
|
||||
frappe._dict(
|
||||
{"mode_of_payment": p.mode_of_payment, "opening_amount": 0, "expected_amount": p.amount}
|
||||
{
|
||||
"mode_of_payment": p.mode_of_payment,
|
||||
"opening_amount": 0,
|
||||
"expected_amount": p.amount,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -62,9 +62,7 @@ class TestPOSClosingEntry(unittest.TestCase):
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
|
||||
pos_inv = create_pos_invoice(
|
||||
rate=3500, do_not_submit=1, item_name="Test Item", without_item_code=1
|
||||
)
|
||||
pos_inv = create_pos_invoice(rate=3500, do_not_submit=1, item_name="Test Item", without_item_code=1)
|
||||
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
|
||||
pos_inv.submit()
|
||||
|
||||
@@ -211,7 +209,7 @@ def get_test_item_qty(pos_profile):
|
||||
item_group="All Item Groups",
|
||||
)
|
||||
|
||||
test_item_qty = [item for item in test_item_pos["items"] if item["item_code"] == "_Test Item"][
|
||||
0
|
||||
].get("actual_qty")
|
||||
test_item_qty = next(item for item in test_item_pos["items"] if item["item_code"] == "_Test Item").get(
|
||||
"actual_qty"
|
||||
)
|
||||
return test_item_qty
|
||||
|
||||
@@ -183,7 +183,7 @@ class POSInvoice(SalesInvoice):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(POSInvoice, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def validate(self):
|
||||
if not cint(self.is_pos):
|
||||
@@ -308,7 +308,9 @@ class POSInvoice(SalesInvoice):
|
||||
)
|
||||
|
||||
if paid_amt and pay.amount != paid_amt:
|
||||
return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment))
|
||||
return frappe.throw(
|
||||
_("Payment related to {0} is not completed").format(pay.mode_of_payment)
|
||||
)
|
||||
|
||||
def validate_stock_availablility(self):
|
||||
if self.is_return:
|
||||
@@ -328,7 +330,7 @@ class POSInvoice(SalesInvoice):
|
||||
|
||||
available_stock, is_stock_item = get_stock_availability(d.item_code, d.warehouse)
|
||||
|
||||
item_code, warehouse, qty = (
|
||||
item_code, warehouse, _qty = (
|
||||
frappe.bold(d.item_code),
|
||||
frappe.bold(d.warehouse),
|
||||
frappe.bold(d.qty),
|
||||
@@ -408,8 +410,7 @@ class POSInvoice(SalesInvoice):
|
||||
if (
|
||||
self.change_amount
|
||||
and self.account_for_change_amount
|
||||
and frappe.get_cached_value("Account", self.account_for_change_amount, "company")
|
||||
!= self.company
|
||||
and frappe.get_cached_value("Account", self.account_for_change_amount, "company") != self.company
|
||||
):
|
||||
frappe.throw(
|
||||
_("The selected change account {} doesn't belongs to Company {}.").format(
|
||||
|
||||
@@ -245,7 +245,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
self.assertEqual(pos_return.get("payments")[1].amount, -500)
|
||||
|
||||
def test_pos_return_for_serialized_item(self):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
se = make_serialized_item(
|
||||
@@ -287,7 +286,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_partial_pos_returns(self):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
se = make_serialized_item(
|
||||
@@ -359,9 +357,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
)
|
||||
|
||||
pos.set("payments", [])
|
||||
pos.append(
|
||||
"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 50}
|
||||
)
|
||||
pos.append("payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 50})
|
||||
pos.append(
|
||||
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 60, "default": 1}
|
||||
)
|
||||
@@ -379,7 +375,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
self.assertRaises(frappe.ValidationError, inv.insert)
|
||||
|
||||
def test_serialized_item_transaction(self):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
se = make_serialized_item(
|
||||
@@ -434,7 +429,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
self.assertRaises(frappe.ValidationError, pos2.submit)
|
||||
|
||||
def test_delivered_serialized_item_transaction(self):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
se = make_serialized_item(
|
||||
@@ -583,9 +577,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
|
||||
|
||||
create_records()
|
||||
frappe.db.set_value(
|
||||
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
|
||||
)
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||
before_lp_details = get_loyalty_program_details_with_points(
|
||||
"Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty"
|
||||
)
|
||||
@@ -659,9 +651,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
rounded_total = frappe.db.get_value(
|
||||
"Sales Invoice", pos_inv.consolidated_invoice, "rounded_total"
|
||||
)
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 3470)
|
||||
|
||||
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
|
||||
@@ -708,9 +698,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
rounded_total = frappe.db.get_value(
|
||||
"Sales Invoice", pos_inv.consolidated_invoice, "rounded_total"
|
||||
)
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 840)
|
||||
|
||||
def test_merging_with_validate_selling_price(self):
|
||||
@@ -762,9 +750,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv2.load_from_db()
|
||||
rounded_total = frappe.db.get_value(
|
||||
"Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total"
|
||||
)
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 400)
|
||||
|
||||
def test_pos_batch_reservation(self):
|
||||
@@ -788,9 +774,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||
|
||||
# POS Invoice 1, for the batch without bundle
|
||||
pos_inv1 = create_pos_invoice(
|
||||
item="_BATCH ITEM Test For Reserve", rate=300, qty=15, do_not_save=1
|
||||
)
|
||||
pos_inv1 = create_pos_invoice(item="_BATCH ITEM Test For Reserve", rate=300, qty=15, do_not_save=1)
|
||||
|
||||
pos_inv1.items[0].batch_no = batch_no
|
||||
pos_inv1.save()
|
||||
@@ -800,9 +784,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
self.assertFalse(pos_inv1.items[0].serial_and_batch_bundle)
|
||||
|
||||
batches = get_auto_batch_nos(
|
||||
frappe._dict(
|
||||
{"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"}
|
||||
)
|
||||
frappe._dict({"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"})
|
||||
)
|
||||
|
||||
for batch in batches:
|
||||
@@ -817,9 +799,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
self.assertTrue(pos_inv2.items[0].serial_and_batch_bundle)
|
||||
|
||||
batches = get_auto_batch_nos(
|
||||
frappe._dict(
|
||||
{"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"}
|
||||
)
|
||||
frappe._dict({"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"})
|
||||
)
|
||||
|
||||
for batch in batches:
|
||||
@@ -896,19 +876,19 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
pos_inv = create_pos_invoice(qty=1, do_not_submit=1)
|
||||
pos_inv.items[0].rate = 300
|
||||
pos_inv.save()
|
||||
self.assertEquals(pos_inv.items[0].discount_percentage, 10)
|
||||
self.assertEqual(pos_inv.items[0].discount_percentage, 10)
|
||||
# rate shouldn't change
|
||||
self.assertEquals(pos_inv.items[0].rate, 405)
|
||||
self.assertEqual(pos_inv.items[0].rate, 405)
|
||||
|
||||
pos_inv.ignore_pricing_rule = 1
|
||||
pos_inv.save()
|
||||
self.assertEquals(pos_inv.ignore_pricing_rule, 1)
|
||||
self.assertEqual(pos_inv.ignore_pricing_rule, 1)
|
||||
# rate should reset since pricing rules are ignored
|
||||
self.assertEquals(pos_inv.items[0].rate, 450)
|
||||
self.assertEqual(pos_inv.items[0].rate, 450)
|
||||
|
||||
pos_inv.items[0].rate = 300
|
||||
pos_inv.save()
|
||||
self.assertEquals(pos_inv.items[0].rate, 300)
|
||||
self.assertEqual(pos_inv.items[0].rate, 300)
|
||||
|
||||
finally:
|
||||
item_price.delete()
|
||||
@@ -920,7 +900,6 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
init_user_and_profile,
|
||||
)
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
frappe.db.savepoint("before_test_delivered_serial_no_case")
|
||||
|
||||
@@ -54,7 +54,7 @@ class POSInvoiceMergeLog(Document):
|
||||
for key, value in pos_occurences.items():
|
||||
if len(value) > 1:
|
||||
error_list.append(
|
||||
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
|
||||
_(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}")
|
||||
)
|
||||
|
||||
if error_list:
|
||||
@@ -81,7 +81,9 @@ class POSInvoiceMergeLog(Document):
|
||||
bold_pos_invoice = frappe.bold(d.pos_invoice)
|
||||
bold_status = frappe.bold(status)
|
||||
if docstatus != 1:
|
||||
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, bold_pos_invoice))
|
||||
frappe.throw(
|
||||
_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, bold_pos_invoice)
|
||||
)
|
||||
if status == "Consolidated":
|
||||
frappe.throw(
|
||||
_("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status)
|
||||
@@ -100,15 +102,17 @@ class POSInvoiceMergeLog(Document):
|
||||
d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated
|
||||
)
|
||||
msg += " "
|
||||
msg += _("Original invoice should be consolidated before or along with the return invoice.")
|
||||
msg += _(
|
||||
"Original invoice should be consolidated before or along with the return invoice."
|
||||
)
|
||||
msg += "<br><br>"
|
||||
msg += _("You can add original invoice {} manually to proceed.").format(bold_return_against)
|
||||
msg += _("You can add original invoice {} manually to proceed.").format(
|
||||
bold_return_against
|
||||
)
|
||||
frappe.throw(msg)
|
||||
|
||||
def on_submit(self):
|
||||
pos_invoice_docs = [
|
||||
frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
|
||||
]
|
||||
pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
||||
|
||||
returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
|
||||
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
|
||||
@@ -124,9 +128,7 @@ class POSInvoiceMergeLog(Document):
|
||||
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
|
||||
|
||||
def on_cancel(self):
|
||||
pos_invoice_docs = [
|
||||
frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
|
||||
]
|
||||
pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
||||
|
||||
self.update_pos_invoices(pos_invoice_docs)
|
||||
self.cancel_linked_invoices()
|
||||
@@ -217,7 +219,9 @@ class POSInvoiceMergeLog(Document):
|
||||
for t in taxes:
|
||||
if t.account_head == tax.account_head and t.cost_center == tax.cost_center:
|
||||
t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount)
|
||||
t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount)
|
||||
t.base_tax_amount = flt(t.base_tax_amount) + flt(
|
||||
tax.base_tax_amount_after_discount_amount
|
||||
)
|
||||
update_item_wise_tax_detail(t, tax)
|
||||
found = True
|
||||
if not found:
|
||||
@@ -333,9 +337,7 @@ def update_item_wise_tax_detail(consolidate_tax_row, tax_row):
|
||||
else:
|
||||
consolidated_tax_detail.update({item_code: [tax_data[0], tax_data[1]]})
|
||||
|
||||
consolidate_tax_row.item_wise_tax_detail = json.dumps(
|
||||
consolidated_tax_detail, separators=(",", ":")
|
||||
)
|
||||
consolidate_tax_row.item_wise_tax_detail = json.dumps(consolidated_tax_detail, separators=(",", ":"))
|
||||
|
||||
|
||||
def get_all_unconsolidated_invoices():
|
||||
@@ -380,9 +382,7 @@ def consolidate_pos_invoices(pos_invoices=None, closing_entry=None):
|
||||
|
||||
if len(invoices) >= 10 and closing_entry:
|
||||
closing_entry.set_status(update=True, status="Queued")
|
||||
enqueue_job(
|
||||
create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry
|
||||
)
|
||||
enqueue_job(create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry)
|
||||
else:
|
||||
create_merge_logs(invoice_by_customer, closing_entry)
|
||||
|
||||
@@ -431,9 +431,7 @@ def split_invoices(invoices):
|
||||
if not item.serial_no and not item.serial_and_batch_bundle:
|
||||
continue
|
||||
|
||||
return_against_is_added = any(
|
||||
d for d in _invoices if d.pos_invoice == pos_invoice.return_against
|
||||
)
|
||||
return_against_is_added = any(d for d in _invoices if d.pos_invoice == pos_invoice.return_against)
|
||||
if return_against_is_added:
|
||||
break
|
||||
|
||||
@@ -482,7 +480,7 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
|
||||
|
||||
if closing_entry:
|
||||
closing_entry.set_status(update=True, status="Failed")
|
||||
if type(error_message) == list:
|
||||
if isinstance(error_message, list):
|
||||
error_message = frappe.json.dumps(error_message)
|
||||
closing_entry.db_set("error_message", error_message)
|
||||
raise
|
||||
@@ -533,7 +531,7 @@ def enqueue_job(job, **kwargs):
|
||||
timeout=10000,
|
||||
event="processing_merge_logs",
|
||||
job_id=job_id,
|
||||
now=frappe.conf.developer_mode or frappe.flags.in_test
|
||||
now=frappe.conf.developer_mode or frappe.flags.in_test,
|
||||
)
|
||||
|
||||
if job == create_merge_logs:
|
||||
|
||||
@@ -31,15 +31,11 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
pos_inv.submit()
|
||||
|
||||
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
||||
pos_inv2.append(
|
||||
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}
|
||||
)
|
||||
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
|
||||
pos_inv2.submit()
|
||||
|
||||
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
|
||||
pos_inv3.append(
|
||||
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}
|
||||
)
|
||||
pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
|
||||
pos_inv3.submit()
|
||||
|
||||
consolidate_pos_invoices()
|
||||
@@ -68,15 +64,11 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
pos_inv.submit()
|
||||
|
||||
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
||||
pos_inv2.append(
|
||||
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}
|
||||
)
|
||||
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
|
||||
pos_inv2.submit()
|
||||
|
||||
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
|
||||
pos_inv3.append(
|
||||
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}
|
||||
)
|
||||
pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
|
||||
pos_inv3.submit()
|
||||
|
||||
pos_inv_cn = make_sales_return(pos_inv.name)
|
||||
@@ -312,7 +304,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
init_user_and_profile()
|
||||
|
||||
item_rates = [69, 59, 29]
|
||||
for i in [1, 2]:
|
||||
for _i in [1, 2]:
|
||||
inv = create_pos_invoice(is_return=1, do_not_save=1)
|
||||
inv.items = []
|
||||
for rate in item_rates:
|
||||
@@ -406,7 +398,6 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
The second and third POS Invoice should be consolidated with a single Merge Log
|
||||
"""
|
||||
|
||||
from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||
|
||||
@@ -180,10 +180,8 @@ class POSProfile(Document):
|
||||
condition = " where pfu.default = 1 "
|
||||
|
||||
pos_view_users = frappe.db.sql_list(
|
||||
"""select pfu.user
|
||||
from `tabPOS Profile User` as pfu {0}""".format(
|
||||
condition
|
||||
)
|
||||
f"""select pfu.user
|
||||
from `tabPOS Profile User` as pfu {condition}"""
|
||||
)
|
||||
|
||||
for user in pos_view_users:
|
||||
@@ -210,16 +208,13 @@ def get_item_groups(pos_profile):
|
||||
def get_child_nodes(group_type, root):
|
||||
lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"])
|
||||
return frappe.db.sql(
|
||||
""" Select name, lft, rgt from `tab{tab}` where
|
||||
lft >= {lft} and rgt <= {rgt} order by lft""".format(
|
||||
tab=group_type, lft=lft, rgt=rgt
|
||||
),
|
||||
f""" Select name, lft, rgt from `tab{group_type}` where
|
||||
lft >= {lft} and rgt <= {rgt} order by lft""",
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
|
||||
def required_accounting_dimensions():
|
||||
|
||||
p = frappe.qb.DocType("Accounting Dimension")
|
||||
c = frappe.qb.DocType("Accounting Dimension Detail")
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import frappe
|
||||
|
||||
from erpnext.accounts.doctype.pos_profile.pos_profile import (
|
||||
get_child_nodes,
|
||||
required_accounting_dimensions,
|
||||
)
|
||||
from erpnext.stock.get_item_details import get_pos_profile
|
||||
|
||||
@@ -55,11 +54,9 @@ def get_customers_list(pos_profile=None):
|
||||
|
||||
return (
|
||||
frappe.db.sql(
|
||||
""" select name, customer_name, customer_group,
|
||||
f""" select name, customer_name, customer_group,
|
||||
territory, customer_pos_id from tabCustomer where disabled = 0
|
||||
and {cond}""".format(
|
||||
cond=cond
|
||||
),
|
||||
and {cond}""",
|
||||
tuple(customer_groups),
|
||||
as_dict=1,
|
||||
)
|
||||
@@ -78,7 +75,7 @@ def get_items_list(pos_profile, company):
|
||||
cond = "and i.item_group in (%s)" % (", ".join(["%s"] * len(args_list)))
|
||||
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
f"""
|
||||
select
|
||||
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
|
||||
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
|
||||
@@ -91,10 +88,8 @@ def get_items_list(pos_profile, company):
|
||||
where
|
||||
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 and i.is_fixed_asset = 0
|
||||
{cond}
|
||||
""".format(
|
||||
cond=cond
|
||||
),
|
||||
tuple([company] + args_list),
|
||||
""",
|
||||
tuple([company, *args_list]),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
|
||||
@@ -186,9 +186,9 @@ class PricingRule(Document):
|
||||
|
||||
if self.priority and cint(self.priority) == 1:
|
||||
throw(
|
||||
_("As the field {0} is enabled, the value of the field {1} should be more than 1.").format(
|
||||
frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")
|
||||
)
|
||||
_(
|
||||
"As the field {0} is enabled, the value of the field {1} should be more than 1."
|
||||
).format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))
|
||||
)
|
||||
|
||||
def validate_applicable_for_selling_or_buying(self):
|
||||
@@ -458,9 +458,11 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False):
|
||||
)
|
||||
|
||||
if pricing_rule.apply_rule_on_other_items:
|
||||
item_details["apply_rule_on_other_items"] = json.dumps(pricing_rule.apply_rule_on_other_items)
|
||||
item_details["apply_rule_on_other_items"] = json.dumps(
|
||||
pricing_rule.apply_rule_on_other_items
|
||||
)
|
||||
|
||||
if pricing_rule.coupon_code_based == 1 and args.coupon_code == None:
|
||||
if pricing_rule.coupon_code_based == 1 and args.coupon_code is None:
|
||||
return item_details
|
||||
|
||||
if not pricing_rule.validate_applied_rule:
|
||||
@@ -504,7 +506,6 @@ def update_args_for_pricing_rule(args):
|
||||
|
||||
if args.transaction_type == "selling":
|
||||
if args.customer and not (args.customer_group and args.territory):
|
||||
|
||||
if args.quotation_to and args.quotation_to != "Customer":
|
||||
customer = frappe._dict()
|
||||
else:
|
||||
@@ -535,9 +536,9 @@ def get_pricing_rule_details(args, pricing_rule):
|
||||
def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
||||
|
||||
if (
|
||||
pricing_rule.margin_type in ["Amount", "Percentage"] and pricing_rule.currency == args.currency
|
||||
) or (pricing_rule.margin_type == "Percentage"):
|
||||
if (pricing_rule.margin_type in ["Amount", "Percentage"] and pricing_rule.currency == args.currency) or (
|
||||
pricing_rule.margin_type == "Percentage"
|
||||
):
|
||||
item_details.margin_type = pricing_rule.margin_type
|
||||
item_details.has_margin = True
|
||||
|
||||
@@ -685,7 +686,7 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
||||
return frappe.get_all(
|
||||
"UOM Conversion Detail",
|
||||
filters={"parent": ("in", items), "uom": ("like", "{0}%".format(txt))},
|
||||
filters={"parent": ("in", items), "uom": ("like", f"{txt}%")},
|
||||
fields=["distinct uom"],
|
||||
as_list=1,
|
||||
)
|
||||
|
||||
@@ -103,8 +103,6 @@ class TestPricingRule(unittest.TestCase):
|
||||
self.assertEqual(details.get("discount_percentage"), 15)
|
||||
|
||||
def test_pricing_rule_for_margin(self):
|
||||
from frappe import MandatoryError
|
||||
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
|
||||
test_record = {
|
||||
@@ -205,8 +203,6 @@ class TestPricingRule(unittest.TestCase):
|
||||
self.assertEqual(details.get("discount_percentage"), 10)
|
||||
|
||||
def test_pricing_rule_for_variants(self):
|
||||
from frappe import MandatoryError
|
||||
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
|
||||
if not frappe.db.exists("Item", "Test Variant PRT"):
|
||||
@@ -1181,8 +1177,7 @@ def delete_existing_pricing_rules():
|
||||
"Pricing Rule Item Group",
|
||||
"Pricing Rule Brand",
|
||||
]:
|
||||
|
||||
frappe.db.sql("delete from `tab{0}`".format(doctype))
|
||||
frappe.db.sql(f"delete from `tab{doctype}`")
|
||||
|
||||
|
||||
def make_item_price(item, price_list_name, item_price):
|
||||
|
||||
@@ -101,14 +101,12 @@ def _get_pricing_rules(apply_on, args, values):
|
||||
if not args.get(apply_on_field):
|
||||
return []
|
||||
|
||||
child_doc = "`tabPricing Rule {0}`".format(apply_on)
|
||||
child_doc = f"`tabPricing Rule {apply_on}`"
|
||||
|
||||
conditions = item_variant_condition = item_conditions = ""
|
||||
values[apply_on_field] = args.get(apply_on_field)
|
||||
if apply_on_field in ["item_code", "brand"]:
|
||||
item_conditions = "{child_doc}.{apply_on_field}= %({apply_on_field})s".format(
|
||||
child_doc=child_doc, apply_on_field=apply_on_field
|
||||
)
|
||||
item_conditions = f"{child_doc}.{apply_on_field}= %({apply_on_field})s"
|
||||
|
||||
if apply_on_field == "item_code":
|
||||
if args.get("uom", None):
|
||||
@@ -121,23 +119,19 @@ def _get_pricing_rules(apply_on, args, values):
|
||||
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
|
||||
|
||||
if args.variant_of:
|
||||
item_variant_condition = " or {child_doc}.item_code=%(variant_of)s ".format(
|
||||
child_doc=child_doc
|
||||
)
|
||||
item_variant_condition = f" or {child_doc}.item_code=%(variant_of)s "
|
||||
values["variant_of"] = args.variant_of
|
||||
elif apply_on_field == "item_group":
|
||||
item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
|
||||
if args.get("uom", None):
|
||||
item_conditions += (
|
||||
" and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format(
|
||||
child_doc=child_doc, item_uom=args.get("uom")
|
||||
)
|
||||
item_conditions += " and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format(
|
||||
child_doc=child_doc, item_uom=args.get("uom")
|
||||
)
|
||||
|
||||
conditions += get_other_conditions(conditions, values, args)
|
||||
warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`")
|
||||
if warehouse_conditions:
|
||||
warehouse_conditions = " and {0}".format(warehouse_conditions)
|
||||
warehouse_conditions = f" and {warehouse_conditions}"
|
||||
|
||||
if not args.price_list:
|
||||
args.price_list = None
|
||||
@@ -163,7 +157,7 @@ def _get_pricing_rules(apply_on, args, values):
|
||||
item_variant_condition=item_variant_condition,
|
||||
transaction_type=args.transaction_type,
|
||||
warehouse_cond=warehouse_conditions,
|
||||
apply_on_other_field="other_{0}".format(apply_on_field),
|
||||
apply_on_other_field=f"other_{apply_on_field}",
|
||||
conditions=conditions,
|
||||
),
|
||||
values,
|
||||
@@ -202,14 +196,13 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True):
|
||||
frappe.throw(_("Invalid {0}").format(args.get(field)))
|
||||
|
||||
parent_groups = frappe.db.sql_list(
|
||||
"""select name from `tab%s`
|
||||
where lft<=%s and rgt>=%s"""
|
||||
% (parenttype, "%s", "%s"),
|
||||
"""select name from `tab{}`
|
||||
where lft<={} and rgt>={}""".format(parenttype, "%s", "%s"),
|
||||
(lft, rgt),
|
||||
)
|
||||
|
||||
if parenttype in ["Customer Group", "Item Group", "Territory"]:
|
||||
parent_field = "parent_{0}".format(frappe.scrub(parenttype))
|
||||
parent_field = f"parent_{frappe.scrub(parenttype)}"
|
||||
root_name = frappe.db.get_list(
|
||||
parenttype,
|
||||
{"is_group": 1, parent_field: ("is", "not set")},
|
||||
@@ -235,10 +228,10 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True):
|
||||
def get_other_conditions(conditions, values, args):
|
||||
for field in ["company", "customer", "supplier", "campaign", "sales_partner"]:
|
||||
if args.get(field):
|
||||
conditions += " and ifnull(`tabPricing Rule`.{0}, '') in (%({1})s, '')".format(field, field)
|
||||
conditions += f" and ifnull(`tabPricing Rule`.{field}, '') in (%({field})s, '')"
|
||||
values[field] = args.get(field)
|
||||
else:
|
||||
conditions += " and ifnull(`tabPricing Rule`.{0}, '') = ''".format(field)
|
||||
conditions += f" and ifnull(`tabPricing Rule`.{field}, '') = ''"
|
||||
|
||||
for parenttype in ["Customer Group", "Territory", "Supplier Group"]:
|
||||
group_condition = _get_tree_conditions(args, parenttype, "`tabPricing Rule`")
|
||||
@@ -510,7 +503,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
|
||||
"transaction_date" if frappe.get_meta(doctype).has_field("transaction_date") else "posting_date"
|
||||
)
|
||||
|
||||
child_doctype = "{0} Item".format(doctype)
|
||||
child_doctype = f"{doctype} Item"
|
||||
apply_on = frappe.scrub(pr_doc.get("apply_on"))
|
||||
|
||||
values = [pr_doc.valid_from, pr_doc.valid_upto]
|
||||
@@ -520,9 +513,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
|
||||
warehouses = get_child_warehouses(pr_doc.warehouse)
|
||||
|
||||
condition += """ and `tab{child_doc}`.warehouse in ({warehouses})
|
||||
""".format(
|
||||
child_doc=child_doctype, warehouses=",".join(["%s"] * len(warehouses))
|
||||
)
|
||||
""".format(child_doc=child_doctype, warehouses=",".join(["%s"] * len(warehouses)))
|
||||
|
||||
values.extend(warehouses)
|
||||
|
||||
@@ -534,16 +525,14 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
|
||||
values.extend(items)
|
||||
|
||||
data_set = frappe.db.sql(
|
||||
""" SELECT `tab{child_doc}`.stock_qty,
|
||||
`tab{child_doc}`.amount
|
||||
FROM `tab{child_doc}`, `tab{parent_doc}`
|
||||
f""" SELECT `tab{child_doctype}`.stock_qty,
|
||||
`tab{child_doctype}`.amount
|
||||
FROM `tab{child_doctype}`, `tab{doctype}`
|
||||
WHERE
|
||||
`tab{child_doc}`.parent = `tab{parent_doc}`.name and `tab{parent_doc}`.{date_field}
|
||||
between %s and %s and `tab{parent_doc}`.docstatus = 1
|
||||
{condition} group by `tab{child_doc}`.name
|
||||
""".format(
|
||||
parent_doc=doctype, child_doc=child_doctype, condition=condition, date_field=date_field
|
||||
),
|
||||
`tab{child_doctype}`.parent = `tab{doctype}`.name and `tab{doctype}`.{date_field}
|
||||
between %s and %s and `tab{doctype}`.docstatus = 1
|
||||
{condition} group by `tab{child_doctype}`.name
|
||||
""",
|
||||
tuple(values),
|
||||
as_dict=1,
|
||||
)
|
||||
@@ -562,11 +551,9 @@ def apply_pricing_rule_on_transaction(doc):
|
||||
conditions = get_other_conditions(conditions, values, doc)
|
||||
|
||||
pricing_rules = frappe.db.sql(
|
||||
""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
||||
f""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
||||
where {conditions} and `tabPricing Rule`.disable = 0
|
||||
""".format(
|
||||
conditions=conditions
|
||||
),
|
||||
""",
|
||||
values,
|
||||
as_dict=1,
|
||||
)
|
||||
@@ -591,7 +578,9 @@ def apply_pricing_rule_on_transaction(doc):
|
||||
continue
|
||||
|
||||
if (
|
||||
d.validate_applied_rule and doc.get(field) is not None and doc.get(field) < d.get(pr_field)
|
||||
d.validate_applied_rule
|
||||
and doc.get(field) is not None
|
||||
and doc.get(field) < d.get(pr_field)
|
||||
):
|
||||
frappe.msgprint(_("User has not applied rule on the invoice {0}").format(doc.name))
|
||||
else:
|
||||
@@ -660,9 +649,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||
|
||||
qty = pricing_rule.free_qty or 1
|
||||
if pricing_rule.is_recursive:
|
||||
transaction_qty = (
|
||||
args.get("qty") if args else doc.total_qty
|
||||
) - pricing_rule.apply_recursion_over
|
||||
transaction_qty = (args.get("qty") if args else doc.total_qty) - pricing_rule.apply_recursion_over
|
||||
if transaction_qty:
|
||||
qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
|
||||
if pricing_rule.round_free_qty:
|
||||
|
||||
@@ -40,7 +40,7 @@ class TestProcessDeferredAccounting(unittest.TestCase):
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
process_deferred_accounting = doc = frappe.get_doc(
|
||||
process_deferred_accounting = frappe.get_doc(
|
||||
dict(
|
||||
doctype="Process Deferred Accounting",
|
||||
posting_date="2023-07-01",
|
||||
|
||||
@@ -66,9 +66,7 @@ class ProcessPaymentReconciliation(Document):
|
||||
|
||||
def on_cancel(self):
|
||||
self.db_set("status", "Cancelled")
|
||||
log = frappe.db.get_value(
|
||||
"Process Payment Reconciliation Log", filters={"process_pr": self.name}
|
||||
)
|
||||
log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": self.name})
|
||||
if log:
|
||||
frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Cancelled")
|
||||
|
||||
@@ -416,7 +414,6 @@ def reconcile(doc: None | str = None) -> None:
|
||||
# If Payment Entry, update details only for newly linked references
|
||||
# This is for performance
|
||||
if allocations[0].reference_type == "Payment Entry":
|
||||
|
||||
references = [(x.invoice_type, x.invoice_number) for x in allocations]
|
||||
pe = frappe.get_doc(allocations[0].reference_type, allocations[0].reference_name)
|
||||
pe.flags.ignore_validate_update_after_submit = True
|
||||
@@ -430,13 +427,14 @@ def reconcile(doc: None | str = None) -> None:
|
||||
|
||||
# Update reconciled count
|
||||
reconciled_count = frappe.db.count(
|
||||
"Process Payment Reconciliation Log Allocations", filters={"parent": log, "reconciled": True}
|
||||
"Process Payment Reconciliation Log Allocations",
|
||||
filters={"parent": log, "reconciled": True},
|
||||
)
|
||||
frappe.db.set_value(
|
||||
"Process Payment Reconciliation Log", log, "reconciled_entries", reconciled_count
|
||||
)
|
||||
|
||||
except Exception as err:
|
||||
except Exception:
|
||||
# Update the parent doc about the exception
|
||||
frappe.db.rollback()
|
||||
|
||||
@@ -474,15 +472,12 @@ def reconcile(doc: None | str = None) -> None:
|
||||
frappe.db.set_value("Process Payment Reconciliation Log", log, "reconciled", True)
|
||||
frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
|
||||
else:
|
||||
|
||||
if frappe.db.get_value("Process Payment Reconciliation", doc, "status") != "Paused":
|
||||
# trigger next batch in job
|
||||
# generate reconcile job name
|
||||
allocation = get_next_allocation(log)
|
||||
if allocation:
|
||||
reconcile_job_name = (
|
||||
f"process_{doc}_reconcile_allocation_{allocation[0].idx}_{allocation[-1].idx}"
|
||||
)
|
||||
reconcile_job_name = f"process_{doc}_reconcile_allocation_{allocation[0].idx}_{allocation[-1].idx}"
|
||||
else:
|
||||
reconcile_job_name = f"process_{doc}_reconcile"
|
||||
|
||||
@@ -506,7 +501,7 @@ def reconcile(doc: None | str = None) -> None:
|
||||
def is_any_doc_running(for_filter: str | dict | None = None) -> str | None:
|
||||
running_doc = None
|
||||
if for_filter:
|
||||
if type(for_filter) == str:
|
||||
if isinstance(for_filter, str):
|
||||
for_filter = frappe.json.loads(for_filter)
|
||||
|
||||
running_doc = frappe.db.get_value(
|
||||
|
||||
@@ -24,9 +24,7 @@ class ProcessPaymentReconciliationLog(Document):
|
||||
process_pr: DF.Link
|
||||
reconciled: DF.Check
|
||||
reconciled_entries: DF.Int
|
||||
status: DF.Literal[
|
||||
"Running", "Paused", "Reconciled", "Partially Reconciled", "Failed", "Cancelled"
|
||||
]
|
||||
status: DF.Literal["Running", "Paused", "Reconciled", "Partially Reconciled", "Failed", "Cancelled"]
|
||||
total_allocations: DF.Int
|
||||
# end: auto-generated types
|
||||
|
||||
|
||||
@@ -46,9 +46,7 @@ class ProcessStatementOfAccounts(Document):
|
||||
company: DF.Link
|
||||
cost_center: DF.TableMultiSelect[PSOACostCenter]
|
||||
currency: DF.Link | None
|
||||
customer_collection: DF.Literal[
|
||||
"", "Customer Group", "Territory", "Sales Partner", "Sales Person"
|
||||
]
|
||||
customer_collection: DF.Literal["", "Customer Group", "Territory", "Sales Partner", "Sales Person"]
|
||||
customers: DF.Table[ProcessStatementOfAccountsCustomer]
|
||||
enable_auto_email: DF.Check
|
||||
filter_duration: DF.Int
|
||||
@@ -406,9 +404,7 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr
|
||||
{mcond}
|
||||
ORDER BY
|
||||
contact.creation desc
|
||||
""".format(
|
||||
mcond=get_match_cond("Contact")
|
||||
),
|
||||
""".format(mcond=get_match_cond("Contact")),
|
||||
customer_name,
|
||||
)
|
||||
|
||||
@@ -481,9 +477,7 @@ def send_emails(document_name, from_scheduler=False, posting_date=None):
|
||||
else:
|
||||
new_to_date = add_months(new_to_date, 1 if doc.frequency == "Monthly" else 3)
|
||||
new_from_date = add_months(new_to_date, -1 * doc.filter_duration)
|
||||
doc.add_comment(
|
||||
"Comment", "Emails sent on: " + frappe.utils.format_datetime(frappe.utils.now())
|
||||
)
|
||||
doc.add_comment("Comment", "Emails sent on: " + frappe.utils.format_datetime(frappe.utils.now()))
|
||||
if doc.report == "General Ledger":
|
||||
doc.db_set("to_date", new_to_date, commit=True)
|
||||
doc.db_set("from_date", new_from_date, commit=True)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import getdate
|
||||
|
||||
from erpnext.accounts.doctype.subscription.subscription import process_all
|
||||
from erpnext.accounts.doctype.subscription.subscription import DateTimeLikeObject, process_all
|
||||
|
||||
|
||||
class ProcessSubscription(Document):
|
||||
@@ -30,7 +27,7 @@ class ProcessSubscription(Document):
|
||||
|
||||
|
||||
def create_subscription_process(
|
||||
subscription: str | None = None, posting_date: Union[str, datetime.date] | None = None
|
||||
subscription: str | None = None, posting_date: DateTimeLikeObject | None = None
|
||||
):
|
||||
"""Create a new Process Subscription document"""
|
||||
doc = frappe.new_doc("Process Subscription")
|
||||
|
||||
@@ -169,9 +169,7 @@ class PromotionalScheme(Document):
|
||||
docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
|
||||
|
||||
for docname in docnames:
|
||||
if frappe.db.exists(
|
||||
"Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}
|
||||
):
|
||||
if frappe.db.exists("Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}):
|
||||
raise_for_transaction_exists(self.name)
|
||||
|
||||
if docnames and not transaction_exists:
|
||||
@@ -246,7 +244,7 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
|
||||
args = get_args_for_pricing_rule(doc)
|
||||
applicable_for = frappe.scrub(doc.get("applicable_for"))
|
||||
|
||||
for idx, d in enumerate(doc.get(child_doc)):
|
||||
for _idx, d in enumerate(doc.get(child_doc)):
|
||||
if d.name in rules:
|
||||
if not args.get(applicable_for):
|
||||
docname = get_pricing_rule_docname(d)
|
||||
@@ -256,7 +254,14 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
|
||||
for applicable_for_value in args.get(applicable_for):
|
||||
docname = get_pricing_rule_docname(d, applicable_for, applicable_for_value)
|
||||
pr = prepare_pricing_rule(
|
||||
args, doc, child_doc, discount_fields, d, docname, applicable_for, applicable_for_value
|
||||
args,
|
||||
doc,
|
||||
child_doc,
|
||||
discount_fields,
|
||||
d,
|
||||
docname,
|
||||
applicable_for,
|
||||
applicable_for_value,
|
||||
)
|
||||
new_doc.append(pr)
|
||||
|
||||
@@ -282,7 +287,7 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
|
||||
|
||||
|
||||
def get_pricing_rule_docname(
|
||||
row: dict, applicable_for: str = None, applicable_for_value: str = None
|
||||
row: dict, applicable_for: str | None = None, applicable_for_value: str | None = None
|
||||
) -> str:
|
||||
fields = ["promotional_scheme_id", "name"]
|
||||
filters = {"promotional_scheme_id": row.name}
|
||||
|
||||
@@ -227,7 +227,7 @@ class PurchaseInvoice(BuyingController):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PurchaseInvoice, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.status_updater = [
|
||||
{
|
||||
"source_dt": "Purchase Invoice Item",
|
||||
@@ -244,7 +244,7 @@ class PurchaseInvoice(BuyingController):
|
||||
]
|
||||
|
||||
def onload(self):
|
||||
super(PurchaseInvoice, self).onload()
|
||||
super().onload()
|
||||
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||
self.set_onload("supplier_tds", supplier_tds)
|
||||
|
||||
@@ -264,7 +264,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.validate_posting_time()
|
||||
|
||||
super(PurchaseInvoice, self).validate()
|
||||
super().validate()
|
||||
|
||||
if not self.is_return:
|
||||
self.po_required()
|
||||
@@ -324,7 +324,6 @@ class PurchaseInvoice(BuyingController):
|
||||
if flt(self.paid_amount) + flt(self.write_off_amount) - flt(
|
||||
self.get("rounded_total") or self.grand_total
|
||||
) > 1 / (10 ** (self.precision("base_grand_total") + 1)):
|
||||
|
||||
frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total"""))
|
||||
|
||||
def create_remarks(self):
|
||||
@@ -353,7 +352,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.tax_withholding_category = tds_category
|
||||
self.set_onload("supplier_tds", tds_category)
|
||||
|
||||
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
||||
super().set_missing_values(for_validate)
|
||||
|
||||
def validate_credit_to_acc(self):
|
||||
if not self.credit_to:
|
||||
@@ -392,7 +391,7 @@ class PurchaseInvoice(BuyingController):
|
||||
check_on_hold_or_closed_status("Purchase Order", d.purchase_order)
|
||||
|
||||
def validate_with_previous_doc(self):
|
||||
super(PurchaseInvoice, self).validate_with_previous_doc(
|
||||
super().validate_with_previous_doc(
|
||||
{
|
||||
"Purchase Order": {
|
||||
"ref_dn_field": "purchase_order",
|
||||
@@ -440,7 +439,7 @@ class PurchaseInvoice(BuyingController):
|
||||
exc=WarehouseMissingError,
|
||||
)
|
||||
|
||||
super(PurchaseInvoice, self).validate_warehouse()
|
||||
super().validate_warehouse()
|
||||
|
||||
def validate_item_code(self):
|
||||
for d in self.get("items"):
|
||||
@@ -476,7 +475,6 @@ class PurchaseInvoice(BuyingController):
|
||||
or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")
|
||||
)
|
||||
):
|
||||
|
||||
if self.update_stock and item.warehouse and (not item.from_warehouse):
|
||||
if (
|
||||
for_validate
|
||||
@@ -504,12 +502,16 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
if negative_expense_booked_in_pr:
|
||||
if (
|
||||
for_validate and item.expense_account and item.expense_account != stock_not_billed_account
|
||||
for_validate
|
||||
and item.expense_account
|
||||
and item.expense_account != stock_not_billed_account
|
||||
):
|
||||
msg = _(
|
||||
"Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}"
|
||||
).format(
|
||||
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt)
|
||||
item.idx,
|
||||
frappe.bold(stock_not_billed_account),
|
||||
frappe.bold(item.purchase_receipt),
|
||||
)
|
||||
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
||||
|
||||
@@ -518,7 +520,9 @@ class PurchaseInvoice(BuyingController):
|
||||
# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
|
||||
# This is done in cases when Purchase Invoice is created before Purchase Receipt
|
||||
if (
|
||||
for_validate and item.expense_account and item.expense_account != stock_not_billed_account
|
||||
for_validate
|
||||
and item.expense_account
|
||||
and item.expense_account != stock_not_billed_account
|
||||
):
|
||||
msg = _(
|
||||
"Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}."
|
||||
@@ -569,7 +573,6 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def po_required(self):
|
||||
if frappe.db.get_single_value("Buying Settings", "po_required") == "Yes":
|
||||
|
||||
if frappe.get_value(
|
||||
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_order"
|
||||
):
|
||||
@@ -579,7 +582,9 @@ class PurchaseInvoice(BuyingController):
|
||||
if not d.purchase_order:
|
||||
msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
|
||||
msg += "<br><br>"
|
||||
msg += _("To submit the invoice without purchase order please set {0} as {1} in {2}").format(
|
||||
msg += _(
|
||||
"To submit the invoice without purchase order please set {0} as {1} in {2}"
|
||||
).format(
|
||||
frappe.bold(_("Purchase Order Required")),
|
||||
frappe.bold("No"),
|
||||
get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
|
||||
@@ -589,7 +594,6 @@ class PurchaseInvoice(BuyingController):
|
||||
def pr_required(self):
|
||||
stock_items = self.get_stock_items()
|
||||
if frappe.db.get_single_value("Buying Settings", "pr_required") == "Yes":
|
||||
|
||||
if frappe.get_value(
|
||||
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_receipt"
|
||||
):
|
||||
@@ -622,7 +626,8 @@ class PurchaseInvoice(BuyingController):
|
||||
frappe.throw(_("Purchase Order {0} is not submitted").format(d.purchase_order))
|
||||
if d.purchase_receipt:
|
||||
submitted = frappe.db.sql(
|
||||
"select name from `tabPurchase Receipt` where docstatus = 1 and name = %s", d.purchase_receipt
|
||||
"select name from `tabPurchase Receipt` where docstatus = 1 and name = %s",
|
||||
d.purchase_receipt,
|
||||
)
|
||||
if not submitted:
|
||||
frappe.throw(_("Purchase Receipt {0} is not submitted").format(d.purchase_receipt))
|
||||
@@ -670,7 +675,9 @@ class PurchaseInvoice(BuyingController):
|
||||
for item in self.get("items"):
|
||||
if item.purchase_receipt:
|
||||
frappe.throw(
|
||||
_("Stock cannot be updated against Purchase Receipt {0}").format(item.purchase_receipt)
|
||||
_("Stock cannot be updated against Purchase Receipt {0}").format(
|
||||
item.purchase_receipt
|
||||
)
|
||||
)
|
||||
|
||||
def validate_for_repost(self):
|
||||
@@ -680,7 +687,7 @@ class PurchaseInvoice(BuyingController):
|
||||
validate_docs_for_deferred_accounting([], [self.name])
|
||||
|
||||
def on_submit(self):
|
||||
super(PurchaseInvoice, self).on_submit()
|
||||
super().on_submit()
|
||||
|
||||
self.check_prev_docstatus()
|
||||
|
||||
@@ -717,9 +724,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.update_stock == 1:
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
if (
|
||||
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
|
||||
):
|
||||
if frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction":
|
||||
self.update_project()
|
||||
|
||||
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||
@@ -911,7 +916,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if flt(item.base_net_amount):
|
||||
account_currency = get_account_currency(item.expense_account)
|
||||
if item.item_code:
|
||||
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
||||
frappe.get_cached_value("Item", item.item_code, "asset_category")
|
||||
|
||||
if (
|
||||
self.update_stock
|
||||
@@ -1016,7 +1021,9 @@ class PurchaseInvoice(BuyingController):
|
||||
if flt(item.rm_supp_cost):
|
||||
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
|
||||
if not supplier_warehouse_account:
|
||||
frappe.throw(_("Please set account in Warehouse {0}").format(self.supplier_warehouse))
|
||||
frappe.throw(
|
||||
_("Please set account in Warehouse {0}").format(self.supplier_warehouse)
|
||||
)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
@@ -1066,10 +1073,9 @@ class PurchaseInvoice(BuyingController):
|
||||
and self.conversion_rate != exchange_rate_map[item.purchase_receipt]
|
||||
and item.net_rate == net_rate_map[item.pr_detail]
|
||||
):
|
||||
|
||||
discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * (
|
||||
exchange_rate_map[item.purchase_receipt] - self.conversion_rate
|
||||
)
|
||||
discrepancy_caused_by_exchange_rate_difference = (
|
||||
item.qty * item.net_rate
|
||||
) * (exchange_rate_map[item.purchase_receipt] - self.conversion_rate)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
@@ -1111,7 +1117,7 @@ class PurchaseInvoice(BuyingController):
|
||||
(item.purchase_receipt, valuation_tax_accounts),
|
||||
)
|
||||
|
||||
stock_rbnb = (
|
||||
(
|
||||
self.get_company_default("asset_received_but_not_billed")
|
||||
if item.is_fixed_asset
|
||||
else self.stock_received_but_not_billed
|
||||
@@ -1150,7 +1156,9 @@ class PurchaseInvoice(BuyingController):
|
||||
default_provisional_account = self.get_company_default("default_provisional_account")
|
||||
provisional_accounts = set(
|
||||
[
|
||||
d.provisional_expense_account if d.provisional_expense_account else default_provisional_account
|
||||
d.provisional_expense_account
|
||||
if d.provisional_expense_account
|
||||
else default_provisional_account
|
||||
for d in pr_items
|
||||
]
|
||||
)
|
||||
@@ -1207,9 +1215,7 @@ class PurchaseInvoice(BuyingController):
|
||||
},
|
||||
)
|
||||
|
||||
def make_stock_adjustment_entry(
|
||||
self, gl_entries, item, voucher_wise_stock_value, account_currency
|
||||
):
|
||||
def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
|
||||
net_amt_precision = item.precision("base_net_amount")
|
||||
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
|
||||
|
||||
@@ -1225,7 +1231,6 @@ class PurchaseInvoice(BuyingController):
|
||||
and warehouse_debit_amount
|
||||
!= flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
|
||||
):
|
||||
|
||||
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
|
||||
stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
|
||||
stock_adjustment_amt = warehouse_debit_amount - stock_amount
|
||||
@@ -1448,9 +1453,7 @@ class PurchaseInvoice(BuyingController):
|
||||
# base_rounding_adjustment may become zero due to small precision
|
||||
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
|
||||
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
|
||||
if (
|
||||
not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment
|
||||
):
|
||||
if not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment:
|
||||
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
|
||||
self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
|
||||
)
|
||||
@@ -1473,7 +1476,7 @@ class PurchaseInvoice(BuyingController):
|
||||
def on_cancel(self):
|
||||
check_if_return_invoice_linked_with_payment_entry(self)
|
||||
|
||||
super(PurchaseInvoice, self).on_cancel()
|
||||
super().on_cancel()
|
||||
|
||||
self.check_on_hold_or_closed_status()
|
||||
|
||||
@@ -1504,9 +1507,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.update_stock == 1:
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
if (
|
||||
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
|
||||
):
|
||||
if frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction":
|
||||
self.update_project()
|
||||
self.db_set("status", "Cancelled")
|
||||
|
||||
@@ -1538,9 +1539,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
pj = frappe.qb.DocType("Project")
|
||||
for proj, value in projects.items():
|
||||
res = (
|
||||
frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
|
||||
)
|
||||
res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
|
||||
current_purchase_cost = res and res[0][0] or 0
|
||||
frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
|
||||
|
||||
@@ -1808,9 +1807,7 @@ def get_purchase_document_details(doc):
|
||||
)
|
||||
|
||||
net_rate_map = frappe._dict(
|
||||
frappe.get_all(
|
||||
child_doctype, filters={"name": ("in", items)}, fields=["name", "net_rate"], as_list=1
|
||||
)
|
||||
frappe.get_all(child_doctype, filters={"name": ("in", items)}, fields=["name", "net_rate"], as_list=1)
|
||||
)
|
||||
|
||||
return exchange_rate_map, net_rate_map
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, cint, flt, getdate, nowdate, today
|
||||
@@ -233,7 +231,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
|
||||
supplier.on_hold = 0
|
||||
supplier.save()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
raise Exception
|
||||
@@ -267,7 +265,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
self.assertEqual(pi.on_hold, 0)
|
||||
|
||||
def test_gl_entries_with_perpetual_inventory_against_pr(self):
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
company="_Test Company with perpetual inventory",
|
||||
supplier_warehouse="Work In Progress - TCP1",
|
||||
@@ -318,7 +315,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
]
|
||||
)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_values[gle.account][0], gle.account)
|
||||
self.assertEqual(expected_values[gle.account][1], gle.debit)
|
||||
self.assertEqual(expected_values[gle.account][2], gle.credit)
|
||||
@@ -342,9 +339,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
pi.submit()
|
||||
|
||||
# Get exchnage gain and loss account
|
||||
exchange_gain_loss_account = frappe.db.get_value(
|
||||
"Company", pi.company, "exchange_gain_loss_account"
|
||||
)
|
||||
exchange_gain_loss_account = frappe.db.get_value("Company", pi.company, "exchange_gain_loss_account")
|
||||
|
||||
# fetching the latest GL Entry with exchange gain and loss account account
|
||||
amount = frappe.db.get_value(
|
||||
@@ -560,12 +555,10 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
project = frappe.get_doc("Project", {"project_name": "_Test Project for Purchase"})
|
||||
|
||||
existing_purchase_cost = frappe.db.sql(
|
||||
"""select sum(base_net_amount)
|
||||
f"""select sum(base_net_amount)
|
||||
from `tabPurchase Invoice Item`
|
||||
where project = '{0}'
|
||||
and docstatus=1""".format(
|
||||
project.name
|
||||
)
|
||||
where project = '{project.name}'
|
||||
and docstatus=1"""
|
||||
)
|
||||
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
|
||||
|
||||
@@ -740,7 +733,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
"credit",
|
||||
"credit_in_account_currency",
|
||||
):
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_values[gle.account][field], gle[field])
|
||||
|
||||
# Check for valid currency
|
||||
@@ -762,7 +755,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
self.assertFalse(gle)
|
||||
|
||||
def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
|
||||
|
||||
pi = make_purchase_invoice(
|
||||
update_stock=1,
|
||||
posting_date=frappe.utils.nowdate(),
|
||||
@@ -791,13 +783,12 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
(d[0], d) for d in [[pi.credit_to, 0.0, 250.0], [stock_in_hand_account, 250.0, 0.0]]
|
||||
)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_gl_entries[gle.account][0], gle.account)
|
||||
self.assertEqual(expected_gl_entries[gle.account][1], gle.debit)
|
||||
self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
|
||||
|
||||
def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
|
||||
|
||||
pi = make_purchase_invoice(
|
||||
update_stock=1,
|
||||
posting_date=frappe.utils.nowdate(),
|
||||
@@ -832,7 +823,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
]
|
||||
)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_gl_entries[gle.account][0], gle.account)
|
||||
self.assertEqual(expected_gl_entries[gle.account][1], gle.debit)
|
||||
self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
|
||||
@@ -904,9 +895,9 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
pi.load_from_db()
|
||||
|
||||
serial_no = get_serial_nos_from_bundle(pi.get("items")[0].serial_and_batch_bundle)[0]
|
||||
rejected_serial_no = get_serial_nos_from_bundle(
|
||||
pi.get("items")[0].rejected_serial_and_batch_bundle
|
||||
)[0]
|
||||
rejected_serial_no = get_serial_nos_from_bundle(pi.get("items")[0].rejected_serial_and_batch_bundle)[
|
||||
0
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
frappe.db.get_value("Serial No", serial_no, "warehouse"),
|
||||
@@ -1036,12 +1027,8 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
|
||||
def test_duplicate_due_date_in_terms(self):
|
||||
pi = make_purchase_invoice(do_not_save=1)
|
||||
pi.append(
|
||||
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
|
||||
)
|
||||
pi.append(
|
||||
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
|
||||
)
|
||||
pi.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
|
||||
pi.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
|
||||
|
||||
self.assertRaises(frappe.ValidationError, pi.insert)
|
||||
|
||||
@@ -1079,9 +1066,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
cost_center = "_Test Cost Center for BS Account - _TC"
|
||||
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
|
||||
|
||||
pi = make_purchase_invoice_against_cost_center(
|
||||
cost_center=cost_center, credit_to="Creditors - _TC"
|
||||
)
|
||||
pi = make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC")
|
||||
self.assertEqual(pi.cost_center, cost_center)
|
||||
|
||||
expected_values = {
|
||||
@@ -1541,9 +1526,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
def test_provisional_accounting_entry(self):
|
||||
setup_provisional_accounting()
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2)
|
||||
)
|
||||
pr = make_purchase_receipt(item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2))
|
||||
|
||||
pi = create_purchase_invoice_from_receipt(pr.name)
|
||||
pi.set_posting_time = 1
|
||||
@@ -1552,7 +1535,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
pi.save()
|
||||
pi.submit()
|
||||
|
||||
self.assertEquals(pr.items[0].provisional_expense_account, "Provision Account - _TC")
|
||||
self.assertEqual(pr.items[0].provisional_expense_account, "Provision Account - _TC")
|
||||
|
||||
# Check GLE for Purchase Invoice
|
||||
expected_gle = [
|
||||
@@ -1579,9 +1562,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
["_Test Account Cost for Goods Sold - _TC", 250, 0, pi.posting_date],
|
||||
]
|
||||
|
||||
check_gl_entries(
|
||||
self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date
|
||||
)
|
||||
check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date)
|
||||
|
||||
toggle_provisional_accounting_setting()
|
||||
|
||||
@@ -1630,9 +1611,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
["_Test Account Cost for Goods Sold - _TC", 5000, 0, pi.posting_date],
|
||||
]
|
||||
|
||||
check_gl_entries(
|
||||
self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date
|
||||
)
|
||||
check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date)
|
||||
|
||||
toggle_provisional_accounting_setting()
|
||||
|
||||
@@ -1678,9 +1657,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
def test_adjust_incoming_rate(self):
|
||||
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
|
||||
|
||||
frappe.db.set_single_value(
|
||||
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1
|
||||
)
|
||||
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
|
||||
|
||||
# Increase the cost of the item
|
||||
|
||||
@@ -1732,9 +1709,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
)
|
||||
self.assertEqual(stock_value_difference, 50)
|
||||
|
||||
frappe.db.set_single_value(
|
||||
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0
|
||||
)
|
||||
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
|
||||
|
||||
# Don't adjust incoming rate
|
||||
|
||||
@@ -1764,7 +1739,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
|
||||
|
||||
def test_item_less_defaults(self):
|
||||
|
||||
pi = frappe.new_doc("Purchase Invoice")
|
||||
pi.supplier = "_Test Supplier"
|
||||
pi.company = "_Test Company"
|
||||
@@ -2301,7 +2275,7 @@ def make_purchase_invoice(**args):
|
||||
pi.cost_center = args.parent_cost_center
|
||||
|
||||
bundle_id = None
|
||||
if not args.use_serial_batch_fields and ((args.get("batch_no") or args.get("serial_no"))):
|
||||
if not args.use_serial_batch_fields and (args.get("batch_no") or args.get("serial_no")):
|
||||
batches = {}
|
||||
qty = args.qty if args.qty is not None else 5
|
||||
item_code = args.item or args.item_code or "_Test Item"
|
||||
@@ -2450,9 +2424,7 @@ def setup_provisional_accounting(**args):
|
||||
parent_account=args.parent_account or "Current Liabilities - _TC",
|
||||
company=company,
|
||||
)
|
||||
toggle_provisional_accounting_setting(
|
||||
enable=1, company=company, provisional_account=provisional_account
|
||||
)
|
||||
toggle_provisional_accounting_setting(enable=1, company=company, provisional_account=provisional_account)
|
||||
|
||||
|
||||
def toggle_provisional_accounting_setting(**args):
|
||||
|
||||
@@ -37,4 +37,4 @@ class PurchaseTaxesandChargesTemplate(Document):
|
||||
def autoname(self):
|
||||
if self.company and self.title:
|
||||
abbr = frappe.get_cached_value("Company", self.company, "abbr")
|
||||
self.name = "{0} - {1}".format(self.title, abbr)
|
||||
self.name = f"{self.title} - {abbr}"
|
||||
|
||||
@@ -27,7 +27,7 @@ class RepostAccountingLedger(Document):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RepostAccountingLedger, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._allowed_types = get_allowed_types_from_settings()
|
||||
|
||||
def validate(self):
|
||||
@@ -154,7 +154,9 @@ def start_repost(account_repost_doc=str) -> None:
|
||||
doc = frappe.get_doc(x.voucher_type, x.voucher_no)
|
||||
|
||||
if repost_doc.delete_cancelled_entries:
|
||||
frappe.db.delete("GL Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name})
|
||||
frappe.db.delete(
|
||||
"GL Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
|
||||
)
|
||||
frappe.db.delete(
|
||||
"Payment Ledger Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
|
||||
)
|
||||
@@ -200,7 +202,9 @@ def validate_docs_for_deferred_accounting(sales_docs, purchase_docs):
|
||||
if docs_with_deferred_revenue or docs_with_deferred_expense:
|
||||
frappe.throw(
|
||||
_("Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.").format(
|
||||
frappe.bold(comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue]))
|
||||
frappe.bold(
|
||||
comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue])
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ from frappe.utils import add_days, nowdate, today
|
||||
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
|
||||
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import start_repost
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
@@ -40,7 +40,7 @@ def start_payment_ledger_repost(docname=None):
|
||||
|
||||
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", "")
|
||||
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_status", "Completed")
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
frappe.db.rollback()
|
||||
|
||||
traceback = frappe.get_traceback(with_context=True)
|
||||
@@ -75,7 +75,7 @@ class RepostPaymentLedger(Document):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RepostPaymentLedger, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.vouchers = []
|
||||
|
||||
def before_validate(self):
|
||||
|
||||
@@ -235,7 +235,7 @@ class SalesInvoice(SellingController):
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SalesInvoice, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.status_updater = [
|
||||
{
|
||||
"source_dt": "Sales Invoice Item",
|
||||
@@ -272,7 +272,7 @@ class SalesInvoice(SellingController):
|
||||
self.indicator_title = _("Paid")
|
||||
|
||||
def validate(self):
|
||||
super(SalesInvoice, self).validate()
|
||||
super().validate()
|
||||
self.validate_auto_set_posting_time()
|
||||
|
||||
if not (self.is_pos or self.is_debit_note):
|
||||
@@ -475,9 +475,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
self.update_time_sheet(self.name)
|
||||
|
||||
if (
|
||||
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
|
||||
):
|
||||
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
|
||||
update_company_current_month_sales(self.company)
|
||||
self.update_project()
|
||||
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||
@@ -493,9 +491,7 @@ class SalesInvoice(SellingController):
|
||||
and not self.dont_create_loyalty_points
|
||||
):
|
||||
self.make_loyalty_point_entry()
|
||||
elif (
|
||||
self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
|
||||
):
|
||||
elif self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program:
|
||||
against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
|
||||
against_si_doc.delete_loyalty_point_entry()
|
||||
against_si_doc.make_loyalty_point_entry()
|
||||
@@ -524,11 +520,11 @@ class SalesInvoice(SellingController):
|
||||
def check_if_consolidated_invoice(self):
|
||||
# since POS Invoice extends Sales Invoice, we explicitly check if doctype is Sales Invoice
|
||||
if self.doctype == "Sales Invoice" and self.is_consolidated:
|
||||
invoice_or_credit_note = (
|
||||
"consolidated_credit_note" if self.is_return else "consolidated_invoice"
|
||||
)
|
||||
invoice_or_credit_note = "consolidated_credit_note" if self.is_return else "consolidated_invoice"
|
||||
pos_closing_entry = frappe.get_all(
|
||||
"POS Invoice Merge Log", filters={invoice_or_credit_note: self.name}, pluck="pos_closing_entry"
|
||||
"POS Invoice Merge Log",
|
||||
filters={invoice_or_credit_note: self.name},
|
||||
pluck="pos_closing_entry",
|
||||
)
|
||||
if pos_closing_entry and pos_closing_entry[0]:
|
||||
msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format(
|
||||
@@ -540,13 +536,13 @@ class SalesInvoice(SellingController):
|
||||
def before_cancel(self):
|
||||
self.check_if_consolidated_invoice()
|
||||
|
||||
super(SalesInvoice, self).before_cancel()
|
||||
super().before_cancel()
|
||||
self.update_time_sheet(None)
|
||||
|
||||
def on_cancel(self):
|
||||
check_if_return_invoice_linked_with_payment_entry(self)
|
||||
|
||||
super(SalesInvoice, self).on_cancel()
|
||||
super().on_cancel()
|
||||
|
||||
self.check_sales_order_on_hold_or_close("sales_order")
|
||||
|
||||
@@ -578,16 +574,12 @@ class SalesInvoice(SellingController):
|
||||
if self.coupon_code:
|
||||
update_coupon_code_count(self.coupon_code, "cancelled")
|
||||
|
||||
if (
|
||||
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
|
||||
):
|
||||
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
|
||||
update_company_current_month_sales(self.company)
|
||||
self.update_project()
|
||||
if not self.is_return and not self.is_consolidated and self.loyalty_program:
|
||||
self.delete_loyalty_point_entry()
|
||||
elif (
|
||||
self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
|
||||
):
|
||||
elif self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program:
|
||||
against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
|
||||
against_si_doc.delete_loyalty_point_entry()
|
||||
against_si_doc.make_loyalty_point_entry()
|
||||
@@ -694,7 +686,7 @@ class SalesInvoice(SellingController):
|
||||
if not self.due_date and self.customer:
|
||||
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
|
||||
|
||||
super(SalesInvoice, self).set_missing_values(for_validate)
|
||||
super().set_missing_values(for_validate)
|
||||
|
||||
print_format = pos.get("print_format") if pos else None
|
||||
if not print_format and not cint(frappe.db.get_value("Print Format", "POS Invoice", "disabled")):
|
||||
@@ -885,7 +877,8 @@ class SalesInvoice(SellingController):
|
||||
|
||||
if account.report_type != "Balance Sheet":
|
||||
msg = (
|
||||
_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To")) + " "
|
||||
_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To"))
|
||||
+ " "
|
||||
)
|
||||
msg += _(
|
||||
"You can change the parent account to a Balance Sheet account or select a different account."
|
||||
@@ -914,11 +907,16 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
|
||||
def validate_with_previous_doc(self):
|
||||
super(SalesInvoice, self).validate_with_previous_doc(
|
||||
super().validate_with_previous_doc(
|
||||
{
|
||||
"Sales Order": {
|
||||
"ref_dn_field": "sales_order",
|
||||
"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]],
|
||||
"compare_fields": [
|
||||
["customer", "="],
|
||||
["company", "="],
|
||||
["project", "="],
|
||||
["currency", "="],
|
||||
],
|
||||
},
|
||||
"Sales Order Item": {
|
||||
"ref_dn_field": "so_detail",
|
||||
@@ -928,7 +926,12 @@ class SalesInvoice(SellingController):
|
||||
},
|
||||
"Delivery Note": {
|
||||
"ref_dn_field": "delivery_note",
|
||||
"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]],
|
||||
"compare_fields": [
|
||||
["customer", "="],
|
||||
["company", "="],
|
||||
["project", "="],
|
||||
["currency", "="],
|
||||
],
|
||||
},
|
||||
"Delivery Note Item": {
|
||||
"ref_dn_field": "dn_detail",
|
||||
@@ -983,13 +986,14 @@ class SalesInvoice(SellingController):
|
||||
}
|
||||
for key, value in prev_doc_field_map.items():
|
||||
if frappe.db.get_single_value("Selling Settings", value[0]) == "Yes":
|
||||
|
||||
if frappe.get_value("Customer", self.customer, value[0]):
|
||||
continue
|
||||
|
||||
for d in self.get("items"):
|
||||
if d.item_code and not d.get(key.lower().replace(" ", "_")) and not self.get(value[1]):
|
||||
msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1)
|
||||
msgprint(
|
||||
_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1
|
||||
)
|
||||
|
||||
def validate_proj_cust(self):
|
||||
"""check for does customer belong to same project as entered.."""
|
||||
@@ -1011,7 +1015,7 @@ class SalesInvoice(SellingController):
|
||||
frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total"))
|
||||
|
||||
def validate_warehouse(self):
|
||||
super(SalesInvoice, self).validate_warehouse()
|
||||
super().validate_warehouse()
|
||||
|
||||
for d in self.get_item_list():
|
||||
if (
|
||||
@@ -1331,7 +1335,9 @@ class SalesInvoice(SellingController):
|
||||
add_asset_activity(asset.name, _("Asset returned"))
|
||||
|
||||
if asset.calculate_depreciation:
|
||||
posting_date = frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
|
||||
posting_date = frappe.db.get_value(
|
||||
"Sales Invoice", self.return_against, "posting_date"
|
||||
)
|
||||
reverse_depreciation_entry_made_after_disposal(asset, posting_date)
|
||||
notes = _(
|
||||
"This schedule was created when Asset {0} was returned through Sales Invoice {1}."
|
||||
@@ -1379,7 +1385,9 @@ class SalesInvoice(SellingController):
|
||||
else item.deferred_revenue_account
|
||||
)
|
||||
|
||||
amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
|
||||
amount, base_amount = self.get_amount_and_base_amount(
|
||||
item, enable_discount_accounting
|
||||
)
|
||||
|
||||
account_currency = get_account_currency(income_account)
|
||||
gl_entries.append(
|
||||
@@ -1403,7 +1411,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
# expense account gl entries
|
||||
if cint(self.update_stock) and erpnext.is_perpetual_inventory_enabled(self.company):
|
||||
gl_entries += super(SalesInvoice, self).get_gl_entries()
|
||||
gl_entries += super().get_gl_entries()
|
||||
|
||||
def get_asset(self, item):
|
||||
if item.get("asset"):
|
||||
@@ -1466,7 +1474,6 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def make_pos_gl_entries(self, gl_entries):
|
||||
if cint(self.is_pos):
|
||||
|
||||
skip_change_gl_entries = not cint(
|
||||
frappe.db.get_single_value("Accounts Settings", "post_change_gl_entries")
|
||||
)
|
||||
@@ -1624,7 +1631,9 @@ class SalesInvoice(SellingController):
|
||||
"credit_in_account_currency": flt(
|
||||
self.rounding_adjustment, self.precision("rounding_adjustment")
|
||||
),
|
||||
"credit": flt(self.base_rounding_adjustment, self.precision("base_rounding_adjustment")),
|
||||
"credit": flt(
|
||||
self.base_rounding_adjustment, self.precision("base_rounding_adjustment")
|
||||
),
|
||||
"cost_center": round_off_cost_center
|
||||
if self.use_company_roundoff_cost_center
|
||||
else (self.cost_center or round_off_cost_center),
|
||||
@@ -1646,7 +1655,11 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
billed_amt = billed_amt and billed_amt[0][0] or 0
|
||||
frappe.db.set_value(
|
||||
"Delivery Note Item", d.dn_detail, "billed_amt", billed_amt, update_modified=update_modified
|
||||
"Delivery Note Item",
|
||||
d.dn_detail,
|
||||
"billed_amt",
|
||||
billed_amt,
|
||||
update_modified=update_modified,
|
||||
)
|
||||
updated_delivery_notes.append(d.delivery_note)
|
||||
elif d.so_detail:
|
||||
@@ -1701,7 +1714,6 @@ class SalesInvoice(SellingController):
|
||||
and getdate(lp_details.from_date) <= getdate(self.posting_date)
|
||||
and (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date))
|
||||
):
|
||||
|
||||
collection_factor = lp_details.collection_factor if lp_details.collection_factor else 1.0
|
||||
points_earned = cint(eligible_amount / collection_factor)
|
||||
|
||||
@@ -1961,7 +1973,6 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
|
||||
|
||||
|
||||
def update_linked_doc(doctype, name, inter_company_reference):
|
||||
|
||||
if doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
ref_field = "inter_company_invoice_reference"
|
||||
else:
|
||||
@@ -1972,7 +1983,6 @@ def update_linked_doc(doctype, name, inter_company_reference):
|
||||
|
||||
|
||||
def unlink_inter_company_doc(doctype, name, inter_company_reference):
|
||||
|
||||
if doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
ref_doc = "Purchase Invoice" if doctype == "Sales Invoice" else "Sales Invoice"
|
||||
ref_field = "inter_company_invoice_reference"
|
||||
@@ -2147,16 +2157,13 @@ def get_internal_party(parties, link_doctype, doc):
|
||||
|
||||
|
||||
def validate_inter_company_transaction(doc, doctype):
|
||||
|
||||
details = get_inter_company_details(doc, doctype)
|
||||
price_list = (
|
||||
doc.selling_price_list
|
||||
if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]
|
||||
else doc.buying_price_list
|
||||
)
|
||||
valid_price_list = frappe.db.get_value(
|
||||
"Price List", {"name": price_list, "buying": 1, "selling": 1}
|
||||
)
|
||||
valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
|
||||
if not valid_price_list and not doc.is_internal_transfer():
|
||||
frappe.throw(_("Selected Price List should have buying and selling fields checked."))
|
||||
|
||||
@@ -2417,9 +2424,7 @@ def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, wa
|
||||
for item in doc.get("items"):
|
||||
item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
if not item.warehouse and item.get("purchase_order") and item.get("purchase_order_item"):
|
||||
item.warehouse = frappe.db.get_value(
|
||||
"Purchase Order Item", item.purchase_order_item, "warehouse"
|
||||
)
|
||||
item.warehouse = frappe.db.get_value("Purchase Order Item", item.purchase_order_item, "warehouse")
|
||||
|
||||
|
||||
def get_delivery_note_details(internal_reference):
|
||||
@@ -2665,9 +2670,7 @@ def check_if_return_invoice_linked_with_payment_entry(self):
|
||||
# If a Return invoice is linked with payment entry along with other invoices,
|
||||
# the cancellation of the Return causes allocated amount to be greater than paid
|
||||
|
||||
if not frappe.db.get_single_value(
|
||||
"Accounts Settings", "unlink_payment_on_cancellation_of_invoice"
|
||||
):
|
||||
if not frappe.db.get_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice"):
|
||||
return
|
||||
|
||||
payment_entries = []
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import copy
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
@@ -925,7 +924,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
]
|
||||
)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_values[gle.account][0], gle.account)
|
||||
self.assertEqual(expected_values[gle.account][1], gle.debit)
|
||||
self.assertEqual(expected_values[gle.account][2], gle.credit)
|
||||
@@ -951,7 +950,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
write_off_account="_Test Write Off - TCP1",
|
||||
)
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
make_purchase_receipt(
|
||||
company="_Test Company with perpetual inventory",
|
||||
item_code="_Test FG Item",
|
||||
warehouse="Stores - TCP1",
|
||||
@@ -1332,7 +1331,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
expected_values = dict(
|
||||
(d[0], d) for d in [["Debtors - TCP1", 100.0, 0.0], ["Sales - TCP1", 0.0, 100.0]]
|
||||
)
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_values[gle.account][0], gle.account)
|
||||
self.assertEqual(expected_values[gle.account][1], gle.debit)
|
||||
self.assertEqual(expected_values[gle.account][2], gle.credit)
|
||||
@@ -1356,7 +1355,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
[test_records[1]["items"][0]["income_account"], 0.0, 100.0],
|
||||
]
|
||||
)
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_values[gle.account][0], gle.account)
|
||||
self.assertEqual(expected_values[gle.account][1], gle.debit)
|
||||
self.assertEqual(expected_values[gle.account][2], gle.credit)
|
||||
@@ -1431,7 +1430,6 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
si.cancel()
|
||||
|
||||
def test_serialized(self):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
se = make_serialized_item()
|
||||
@@ -1771,7 +1769,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
"credit",
|
||||
"credit_in_account_currency",
|
||||
):
|
||||
for i, gle in enumerate(gl_entries):
|
||||
for _i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(expected_values[gle.account][field], gle[field])
|
||||
|
||||
# cancel
|
||||
@@ -2315,12 +2313,8 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
|
||||
def test_duplicate_due_date_in_terms(self):
|
||||
si = create_sales_invoice(do_not_save=1)
|
||||
si.append(
|
||||
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
|
||||
)
|
||||
si.append(
|
||||
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
|
||||
)
|
||||
si.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
|
||||
si.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
|
||||
|
||||
self.assertRaises(frappe.ValidationError, si.insert)
|
||||
|
||||
@@ -2514,9 +2508,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
item.no_of_months = 12
|
||||
item.save()
|
||||
|
||||
si = create_sales_invoice(
|
||||
item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True
|
||||
)
|
||||
si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True)
|
||||
si.items[0].enable_deferred_revenue = 1
|
||||
si.items[0].service_start_date = "2019-01-16"
|
||||
si.items[0].service_end_date = "2019-03-31"
|
||||
@@ -2837,21 +2829,16 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
item.save()
|
||||
|
||||
sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True)
|
||||
self.assertEqual(
|
||||
sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC"
|
||||
)
|
||||
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
|
||||
|
||||
# Apply discount
|
||||
sales_invoice.apply_discount_on = "Net Total"
|
||||
sales_invoice.discount_amount = 300
|
||||
sales_invoice.save()
|
||||
self.assertEqual(
|
||||
sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC"
|
||||
)
|
||||
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
|
||||
|
||||
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
|
||||
def test_sales_invoice_with_discount_accounting_enabled(self):
|
||||
|
||||
discount_account = create_account(
|
||||
account_name="Discount Account",
|
||||
parent_account="Indirect Expenses - _TC",
|
||||
@@ -2869,7 +2856,6 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
|
||||
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
|
||||
def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self):
|
||||
|
||||
from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import (
|
||||
update_repost_settings,
|
||||
)
|
||||
@@ -2882,7 +2868,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
company="_Test Company",
|
||||
)
|
||||
|
||||
tds_payable_account = create_account(
|
||||
create_account(
|
||||
account_name="TDS Payable",
|
||||
account_type="Tax",
|
||||
parent_account="Duties and Taxes - _TC",
|
||||
@@ -3204,9 +3190,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
"""
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
|
||||
over_billing_allowance = frappe.db.get_single_value(
|
||||
"Accounts Settings", "over_billing_allowance"
|
||||
)
|
||||
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
|
||||
frappe.db.set_single_value("Accounts Settings", "over_billing_allowance", 0)
|
||||
|
||||
dn = create_delivery_note()
|
||||
@@ -3398,7 +3382,7 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
self.assertEqual(len(journals), 1)
|
||||
je_type = frappe.get_cached_value("Journal Entry", journals[0], "voucher_type")
|
||||
self.assertEqual(je_type, "Exchange Gain Or Loss")
|
||||
ledger_outstanding = frappe.db.get_all(
|
||||
frappe.db.get_all(
|
||||
"Payment Ledger Entry",
|
||||
filters={"against_voucher_no": si.name, "delinked": 0},
|
||||
fields=["sum(amount), sum(amount_in_account_currency)"],
|
||||
@@ -3660,41 +3644,6 @@ def set_advance_flag(company, flag, default_account):
|
||||
)
|
||||
|
||||
|
||||
def get_sales_invoice_for_e_invoice():
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
si.naming_series = "INV-2020-.#####"
|
||||
si.items = []
|
||||
si.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": "_Test Item",
|
||||
"uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 2000,
|
||||
"rate": 12,
|
||||
"income_account": "Sales - _TC",
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
},
|
||||
)
|
||||
|
||||
si.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": "_Test Item 2",
|
||||
"uom": "Nos",
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"qty": 420,
|
||||
"rate": 15,
|
||||
"income_account": "Sales - _TC",
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
},
|
||||
)
|
||||
|
||||
return si
|
||||
|
||||
|
||||
def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Sales Invoice"):
|
||||
gl = frappe.qb.DocType("GL Entry")
|
||||
q = (
|
||||
|
||||
@@ -42,7 +42,7 @@ class SalesTaxesandChargesTemplate(Document):
|
||||
def autoname(self):
|
||||
if self.company and self.title:
|
||||
abbr = frappe.get_cached_value("Company", self.company, "abbr")
|
||||
self.name = "{0} - {1}".format(self.title, abbr)
|
||||
self.name = f"{self.title} - {abbr}"
|
||||
|
||||
def set_missing_values(self):
|
||||
for data in self.taxes:
|
||||
@@ -57,10 +57,8 @@ def valdiate_taxes_and_charges_template(doc):
|
||||
|
||||
if doc.is_default == 1:
|
||||
frappe.db.sql(
|
||||
"""update `tab{0}` set is_default = 0
|
||||
where is_default = 1 and name != %s and company = %s""".format(
|
||||
doc.doctype
|
||||
),
|
||||
f"""update `tab{doc.doctype}` set is_default = 0
|
||||
where is_default = 1 and name != %s and company = %s""",
|
||||
(doc.name, doc.company),
|
||||
)
|
||||
|
||||
|
||||
@@ -205,7 +205,9 @@ class ShareTransfer(Document):
|
||||
doc = self.get_shareholder_doc(shareholder)
|
||||
for entry in doc.share_balance:
|
||||
if (
|
||||
entry.share_type != self.share_type or entry.from_no > self.to_no or entry.to_no < self.from_no
|
||||
entry.share_type != self.share_type
|
||||
or entry.from_no > self.to_no
|
||||
or entry.to_no < self.from_no
|
||||
):
|
||||
continue # since query lies outside bounds
|
||||
elif entry.from_no <= self.from_no and entry.to_no >= self.to_no: # both inside
|
||||
@@ -257,7 +259,9 @@ class ShareTransfer(Document):
|
||||
for entry in current_entries:
|
||||
# use spaceage logic here
|
||||
if (
|
||||
entry.share_type != self.share_type or entry.from_no > self.to_no or entry.to_no < self.from_no
|
||||
entry.share_type != self.share_type
|
||||
or entry.from_no > self.to_no
|
||||
or entry.to_no < self.from_no
|
||||
):
|
||||
new_entries.append(entry)
|
||||
continue # since query lies outside bounds
|
||||
@@ -267,7 +271,9 @@ class ShareTransfer(Document):
|
||||
if entry.to_no == self.to_no:
|
||||
pass # nothing to append
|
||||
else:
|
||||
new_entries.append(self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate))
|
||||
new_entries.append(
|
||||
self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate)
|
||||
)
|
||||
else:
|
||||
if entry.to_no == self.to_no:
|
||||
new_entries.append(
|
||||
@@ -277,7 +283,9 @@ class ShareTransfer(Document):
|
||||
new_entries.append(
|
||||
self.return_share_balance_entry(entry.from_no, self.from_no - 1, entry.rate)
|
||||
)
|
||||
new_entries.append(self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate))
|
||||
new_entries.append(
|
||||
self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate)
|
||||
)
|
||||
elif entry.from_no >= self.from_no and entry.to_no <= self.to_no:
|
||||
# split and check
|
||||
pass # nothing to append
|
||||
@@ -309,7 +317,7 @@ class ShareTransfer(Document):
|
||||
def get_shareholder_doc(self, shareholder):
|
||||
# Get Shareholder doc based on the Shareholder name
|
||||
if shareholder:
|
||||
query_filters = {"name": shareholder}
|
||||
pass
|
||||
|
||||
name = frappe.db.get_value("Shareholder", {"name": shareholder}, "name")
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ class ShippingRule(Document):
|
||||
zero_to_values.append(d)
|
||||
elif d.from_value >= d.to_value:
|
||||
throw(
|
||||
_("From value must be less than to value in row {0}").format(d.idx), FromGreaterThanToError
|
||||
_("From value must be less than to value in row {0}").format(d.idx),
|
||||
FromGreaterThanToError,
|
||||
)
|
||||
|
||||
# check if more than two or more rows has To Value = 0
|
||||
@@ -114,9 +115,7 @@ class ShippingRule(Document):
|
||||
|
||||
def get_shipping_amount_from_rules(self, value):
|
||||
for condition in self.get("conditions"):
|
||||
if not condition.to_value or (
|
||||
flt(condition.from_value) <= flt(value) <= flt(condition.to_value)
|
||||
):
|
||||
if not condition.to_value or (flt(condition.from_value) <= flt(value) <= flt(condition.to_value)):
|
||||
return condition.shipping_amount
|
||||
|
||||
return 0.0
|
||||
@@ -131,7 +130,9 @@ class ShippingRule(Document):
|
||||
)
|
||||
if shipping_country not in [d.country for d in self.countries]:
|
||||
frappe.throw(
|
||||
_("Shipping rule not applicable for country {0} in Shipping Address").format(shipping_country)
|
||||
_("Shipping rule not applicable for country {0} in Shipping Address").format(
|
||||
shipping_country
|
||||
)
|
||||
)
|
||||
|
||||
def add_shipping_rule_to_tax_table(self, doc, shipping_amount):
|
||||
@@ -199,11 +200,9 @@ class ShippingRule(Document):
|
||||
messages = []
|
||||
for d1, d2 in overlaps:
|
||||
messages.append(
|
||||
"%s-%s = %s "
|
||||
% (d1.from_value, d1.to_value, fmt_money(d1.shipping_amount, currency=company_currency))
|
||||
f"{d1.from_value}-{d1.to_value} = {fmt_money(d1.shipping_amount, currency=company_currency)} "
|
||||
+ _("and")
|
||||
+ " %s-%s = %s"
|
||||
% (d2.from_value, d2.to_value, fmt_money(d2.shipping_amount, currency=company_currency))
|
||||
+ f" {d2.from_value}-{d2.to_value} = {fmt_money(d2.shipping_amount, currency=company_currency)}"
|
||||
)
|
||||
|
||||
msgprint("\n".join(messages), raise_exception=OverlappingConditionError)
|
||||
|
||||
@@ -45,7 +45,6 @@ class TestShippingRule(unittest.TestCase):
|
||||
|
||||
|
||||
def create_shipping_rule(shipping_rule_type, shipping_rule_name):
|
||||
|
||||
if frappe.db.exists("Shipping Rule", shipping_rule_name):
|
||||
return frappe.get_doc("Shipping Rule", shipping_rule_name)
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Union
|
||||
from datetime import date
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
@@ -37,7 +36,7 @@ class InvoiceNotCancelled(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
DateTimeLikeObject = Union[str, datetime.date]
|
||||
DateTimeLikeObject = str | date
|
||||
|
||||
|
||||
class Subscription(Document):
|
||||
@@ -88,7 +87,7 @@ class Subscription(Document):
|
||||
# update start just before the subscription doc is created
|
||||
self.update_subscription_period(self.start_date)
|
||||
|
||||
def update_subscription_period(self, date: Optional["DateTimeLikeObject"] = None):
|
||||
def update_subscription_period(self, date: DateTimeLikeObject | None = None):
|
||||
"""
|
||||
Subscription period is the period to be billed. This method updates the
|
||||
beginning of the billing period and end of the billing period.
|
||||
@@ -99,15 +98,13 @@ class Subscription(Document):
|
||||
self.current_invoice_start = self.get_current_invoice_start(date)
|
||||
self.current_invoice_end = self.get_current_invoice_end(self.current_invoice_start)
|
||||
|
||||
def _get_subscription_period(self, date: Optional["DateTimeLikeObject"] = None):
|
||||
def _get_subscription_period(self, date: DateTimeLikeObject | None = None):
|
||||
_current_invoice_start = self.get_current_invoice_start(date)
|
||||
_current_invoice_end = self.get_current_invoice_end(_current_invoice_start)
|
||||
|
||||
return _current_invoice_start, _current_invoice_end
|
||||
|
||||
def get_current_invoice_start(
|
||||
self, date: Optional["DateTimeLikeObject"] = None
|
||||
) -> Union[datetime.date, str]:
|
||||
def get_current_invoice_start(self, date: DateTimeLikeObject | None = None) -> DateTimeLikeObject:
|
||||
"""
|
||||
This returns the date of the beginning of the current billing period.
|
||||
If the `date` parameter is not given , it will be automatically set as today's
|
||||
@@ -130,9 +127,7 @@ class Subscription(Document):
|
||||
|
||||
return _current_invoice_start
|
||||
|
||||
def get_current_invoice_end(
|
||||
self, date: Optional["DateTimeLikeObject"] = None
|
||||
) -> Union[datetime.date, str]:
|
||||
def get_current_invoice_end(self, date: DateTimeLikeObject | None = None) -> DateTimeLikeObject:
|
||||
"""
|
||||
This returns the date of the end of the current billing period.
|
||||
If the subscription is in trial period, it will be set as the end of the
|
||||
@@ -174,7 +169,7 @@ class Subscription(Document):
|
||||
return _current_invoice_end
|
||||
|
||||
@staticmethod
|
||||
def validate_plans_billing_cycle(billing_cycle_data: List[Dict[str, str]]) -> None:
|
||||
def validate_plans_billing_cycle(billing_cycle_data: list[dict[str, str]]) -> None:
|
||||
"""
|
||||
Makes sure that all `Subscription Plan` in the `Subscription` have the
|
||||
same billing interval
|
||||
@@ -182,7 +177,7 @@ class Subscription(Document):
|
||||
if billing_cycle_data and len(billing_cycle_data) != 1:
|
||||
frappe.throw(_("You can only have Plans with the same billing cycle in a Subscription"))
|
||||
|
||||
def get_billing_cycle_and_interval(self) -> List[Dict[str, str]]:
|
||||
def get_billing_cycle_and_interval(self) -> list[dict[str, str]]:
|
||||
"""
|
||||
Returns a dict representing the billing interval and cycle for this `Subscription`.
|
||||
You shouldn't need to call this directly. Use `get_billing_cycle` instead.
|
||||
@@ -199,7 +194,7 @@ class Subscription(Document):
|
||||
|
||||
return billing_info
|
||||
|
||||
def get_billing_cycle_data(self) -> Dict[str, int]:
|
||||
def get_billing_cycle_data(self) -> dict[str, int]:
|
||||
"""
|
||||
Returns dict contain the billing cycle data.
|
||||
You shouldn't need to call this directly. Use `get_billing_cycle` instead.
|
||||
@@ -226,15 +221,13 @@ class Subscription(Document):
|
||||
|
||||
return data
|
||||
|
||||
def set_subscription_status(self, posting_date: Optional["DateTimeLikeObject"] = None) -> None:
|
||||
def set_subscription_status(self, posting_date: DateTimeLikeObject | None = None) -> None:
|
||||
"""
|
||||
Sets the status of the `Subscription`
|
||||
"""
|
||||
if self.is_trialling():
|
||||
self.status = "Trialing"
|
||||
elif (
|
||||
self.status == "Active" and self.end_date and getdate(posting_date) > getdate(self.end_date)
|
||||
):
|
||||
elif self.status == "Active" and self.end_date and getdate(posting_date) > getdate(self.end_date):
|
||||
self.status = "Completed"
|
||||
elif self.is_past_grace_period():
|
||||
self.status = self.get_status_for_past_grace_period()
|
||||
@@ -252,7 +245,7 @@ class Subscription(Document):
|
||||
|
||||
@staticmethod
|
||||
def period_has_passed(
|
||||
end_date: Union[str, datetime.date], posting_date: Optional["DateTimeLikeObject"] = None
|
||||
end_date: DateTimeLikeObject, posting_date: DateTimeLikeObject | None = None
|
||||
) -> bool:
|
||||
"""
|
||||
Returns true if the given `end_date` has passed
|
||||
@@ -272,7 +265,7 @@ class Subscription(Document):
|
||||
|
||||
return status
|
||||
|
||||
def is_past_grace_period(self, posting_date: Optional["DateTimeLikeObject"] = None) -> bool:
|
||||
def is_past_grace_period(self, posting_date: DateTimeLikeObject | None = None) -> bool:
|
||||
"""
|
||||
Returns `True` if the grace period for the `Subscription` has passed
|
||||
"""
|
||||
@@ -282,9 +275,7 @@ class Subscription(Document):
|
||||
grace_period = cint(frappe.get_value("Subscription Settings", None, "grace_period"))
|
||||
return getdate(posting_date) >= getdate(add_days(self.current_invoice.due_date, grace_period))
|
||||
|
||||
def current_invoice_is_past_due(
|
||||
self, posting_date: Optional["DateTimeLikeObject"] = None
|
||||
) -> bool:
|
||||
def current_invoice_is_past_due(self, posting_date: DateTimeLikeObject | None = None) -> bool:
|
||||
"""
|
||||
Returns `True` if the current generated invoice is overdue
|
||||
"""
|
||||
@@ -334,14 +325,15 @@ class Subscription(Document):
|
||||
unsupported_plans = []
|
||||
for x in subscription_plan_currencies:
|
||||
if x.currency != party_billing_currency:
|
||||
unsupported_plans.append("{0}".format(get_link_to_form("Subscription Plan", x.name)))
|
||||
unsupported_plans.append("{}".format(get_link_to_form("Subscription Plan", x.name)))
|
||||
|
||||
if unsupported_plans:
|
||||
unsupported_plans = [
|
||||
_(
|
||||
"Below Subscription Plans are of different currency to the party default billing currency/Company currency: {0}"
|
||||
).format(frappe.bold(party_billing_currency))
|
||||
] + unsupported_plans
|
||||
).format(frappe.bold(party_billing_currency)),
|
||||
*unsupported_plans,
|
||||
]
|
||||
|
||||
frappe.throw(
|
||||
unsupported_plans, frappe.ValidationError, "Unsupported Subscription Plans", as_list=True
|
||||
@@ -384,9 +376,9 @@ class Subscription(Document):
|
||||
|
||||
def generate_invoice(
|
||||
self,
|
||||
from_date: Optional[Union[str, datetime.date]] = None,
|
||||
to_date: Optional[Union[str, datetime.date]] = None,
|
||||
posting_date: Optional[Union[str, datetime.date]] = None,
|
||||
from_date: DateTimeLikeObject | None = None,
|
||||
to_date: DateTimeLikeObject | None = None,
|
||||
posting_date: DateTimeLikeObject | None = None,
|
||||
) -> Document:
|
||||
"""
|
||||
Creates a `Invoice` for the `Subscription`, updates `self.invoices` and
|
||||
@@ -397,9 +389,9 @@ class Subscription(Document):
|
||||
|
||||
def create_invoice(
|
||||
self,
|
||||
from_date: Optional[Union[str, datetime.date]] = None,
|
||||
to_date: Optional[Union[str, datetime.date]] = None,
|
||||
posting_date: Optional[Union[str, datetime.date]] = None,
|
||||
from_date: DateTimeLikeObject | None = None,
|
||||
to_date: DateTimeLikeObject | None = None,
|
||||
posting_date: DateTimeLikeObject | None = None,
|
||||
) -> Document:
|
||||
"""
|
||||
Creates a `Invoice`, submits it and returns it
|
||||
@@ -503,9 +495,7 @@ class Subscription(Document):
|
||||
|
||||
return invoice
|
||||
|
||||
def get_items_from_plans(
|
||||
self, plans: List[Dict[str, str]], prorate: Optional[bool] = None
|
||||
) -> List[Dict]:
|
||||
def get_items_from_plans(self, plans: list[dict[str, str]], prorate: bool | None = None) -> list[dict]:
|
||||
"""
|
||||
Returns the `Item`s linked to `Subscription Plan`
|
||||
"""
|
||||
@@ -581,7 +571,7 @@ class Subscription(Document):
|
||||
return items
|
||||
|
||||
@frappe.whitelist()
|
||||
def process(self, posting_date: Optional["DateTimeLikeObject"] = None) -> bool:
|
||||
def process(self, posting_date: DateTimeLikeObject | None = None) -> bool:
|
||||
"""
|
||||
To be called by task periodically. It checks the subscription and takes appropriate action
|
||||
as need be. It calls either of these methods depending the `Subscription` status:
|
||||
@@ -606,7 +596,7 @@ class Subscription(Document):
|
||||
|
||||
self.save()
|
||||
|
||||
def can_generate_new_invoice(self, posting_date: Optional["DateTimeLikeObject"] = None) -> bool:
|
||||
def can_generate_new_invoice(self, posting_date: DateTimeLikeObject | None = None) -> bool:
|
||||
if self.cancelation_date:
|
||||
return False
|
||||
|
||||
@@ -628,8 +618,8 @@ class Subscription(Document):
|
||||
|
||||
def is_current_invoice_generated(
|
||||
self,
|
||||
_current_start_date: Union[datetime.date, str] = None,
|
||||
_current_end_date: Union[datetime.date, str] = None,
|
||||
_current_start_date: DateTimeLikeObject | None = None,
|
||||
_current_end_date: DateTimeLikeObject | None = None,
|
||||
) -> bool:
|
||||
if not (_current_start_date and _current_end_date):
|
||||
_current_start_date, _current_end_date = self._get_subscription_period(
|
||||
@@ -644,13 +634,13 @@ class Subscription(Document):
|
||||
return False
|
||||
|
||||
@property
|
||||
def current_invoice(self) -> Union[Document, None]:
|
||||
def current_invoice(self) -> Document | None:
|
||||
"""
|
||||
Adds property for accessing the current_invoice
|
||||
"""
|
||||
return self.get_current_invoice()
|
||||
|
||||
def get_current_invoice(self) -> Union[Document, None]:
|
||||
def get_current_invoice(self) -> Document | None:
|
||||
"""
|
||||
Returns the most recent generated invoice.
|
||||
"""
|
||||
@@ -675,7 +665,7 @@ class Subscription(Document):
|
||||
self.cancelation_date = nowdate()
|
||||
|
||||
@property
|
||||
def invoices(self) -> List[Dict]:
|
||||
def invoices(self) -> list[dict]:
|
||||
return frappe.get_all(
|
||||
self.invoice_document_type,
|
||||
filters={"subscription": self.name},
|
||||
@@ -725,7 +715,7 @@ class Subscription(Document):
|
||||
self.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def restart_subscription(self, posting_date: Optional["DateTimeLikeObject"] = None) -> None:
|
||||
def restart_subscription(self, posting_date: DateTimeLikeObject | None = None) -> None:
|
||||
"""
|
||||
This sets the subscription as active. The subscription will be made to be like a new
|
||||
subscription and the `Subscription` will lose all the history of generated invoices
|
||||
@@ -745,10 +735,10 @@ def is_prorate() -> int:
|
||||
|
||||
|
||||
def get_prorata_factor(
|
||||
period_end: Union[datetime.date, str],
|
||||
period_start: Union[datetime.date, str],
|
||||
is_prepaid: Optional[int] = None,
|
||||
) -> Union[int, float]:
|
||||
period_end: DateTimeLikeObject,
|
||||
period_start: DateTimeLikeObject,
|
||||
is_prepaid: int | None = None,
|
||||
) -> int | float:
|
||||
if is_prepaid:
|
||||
return 1
|
||||
|
||||
@@ -757,9 +747,7 @@ def get_prorata_factor(
|
||||
return diff / plan_days
|
||||
|
||||
|
||||
def process_all(
|
||||
subscription: str | None = None, posting_date: Optional["DateTimeLikeObject"] = None
|
||||
) -> None:
|
||||
def process_all(subscription: str | None = None, posting_date: DateTimeLikeObject | None = None) -> None:
|
||||
"""
|
||||
Task to updates the status of all `Subscription` apart from those that are cancelled
|
||||
"""
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
@@ -201,9 +200,7 @@ class TestSubscription(FrappeTestCase):
|
||||
|
||||
invoice = subscription.get_current_invoice()
|
||||
diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
|
||||
plan_days = flt(
|
||||
date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1
|
||||
)
|
||||
plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
|
||||
prorate_factor = flt(diff / plan_days)
|
||||
|
||||
self.assertEqual(
|
||||
@@ -249,9 +246,7 @@ class TestSubscription(FrappeTestCase):
|
||||
|
||||
invoice = subscription.get_current_invoice()
|
||||
diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
|
||||
plan_days = flt(
|
||||
date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1
|
||||
)
|
||||
plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
|
||||
prorate_factor = flt(diff / plan_days)
|
||||
|
||||
self.assertEqual(flt(invoice.grand_total, 2), flt(prorate_factor * 900, 2))
|
||||
@@ -385,9 +380,7 @@ class TestSubscription(FrappeTestCase):
|
||||
settings.prorate = 1
|
||||
settings.save()
|
||||
|
||||
subscription = create_subscription(
|
||||
generate_invoice_at="Beginning of the current subscription period"
|
||||
)
|
||||
subscription = create_subscription(generate_invoice_at="Beginning of the current subscription period")
|
||||
subscription.process()
|
||||
subscription.cancel_subscription()
|
||||
|
||||
@@ -546,9 +539,7 @@ def make_plans():
|
||||
billing_interval_count=3,
|
||||
currency="INR",
|
||||
)
|
||||
create_plan(
|
||||
plan_name="_Test Plan Multicurrency", cost=50, billing_interval="Month", currency="USD"
|
||||
)
|
||||
create_plan(plan_name="_Test Plan Multicurrency", cost=50, billing_interval="Month", currency="USD")
|
||||
|
||||
|
||||
def create_plan(**kwargs):
|
||||
@@ -575,9 +566,7 @@ def create_parties():
|
||||
customer = frappe.new_doc("Customer")
|
||||
customer.customer_name = "_Test Subscription Customer"
|
||||
customer.billing_currency = "USD"
|
||||
customer.append(
|
||||
"accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"}
|
||||
)
|
||||
customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"})
|
||||
customer.insert()
|
||||
|
||||
|
||||
@@ -600,9 +589,7 @@ def create_subscription(**kwargs):
|
||||
subscription.additional_discount_percentage = kwargs.get("additional_discount_percentage")
|
||||
subscription.additional_discount_amount = kwargs.get("additional_discount_amount")
|
||||
subscription.follow_calendar_months = kwargs.get("follow_calendar_months")
|
||||
subscription.generate_new_invoices_past_due_date = kwargs.get(
|
||||
"generate_new_invoices_past_due_date"
|
||||
)
|
||||
subscription.generate_new_invoices_past_due_date = kwargs.get("generate_new_invoices_past_due_date")
|
||||
subscription.submit_invoice = kwargs.get("submit_invoice")
|
||||
subscription.days_until_due = kwargs.get("days_until_due")
|
||||
subscription.number_of_days = kwargs.get("number_of_days")
|
||||
|
||||
@@ -112,27 +112,23 @@ class TaxRule(Document):
|
||||
for d in filters:
|
||||
if conds:
|
||||
conds += " and "
|
||||
conds += """ifnull({0}, '') = {1}""".format(d, frappe.db.escape(cstr(filters[d])))
|
||||
conds += f"""ifnull({d}, '') = {frappe.db.escape(cstr(filters[d]))}"""
|
||||
|
||||
if self.from_date and self.to_date:
|
||||
conds += """ and ((from_date > '{from_date}' and from_date < '{to_date}') or
|
||||
(to_date > '{from_date}' and to_date < '{to_date}') or
|
||||
('{from_date}' > from_date and '{from_date}' < to_date) or
|
||||
('{from_date}' = from_date and '{to_date}' = to_date))""".format(
|
||||
from_date=self.from_date, to_date=self.to_date
|
||||
)
|
||||
conds += f""" and ((from_date > '{self.from_date}' and from_date < '{self.to_date}') or
|
||||
(to_date > '{self.from_date}' and to_date < '{self.to_date}') or
|
||||
('{self.from_date}' > from_date and '{self.from_date}' < to_date) or
|
||||
('{self.from_date}' = from_date and '{self.to_date}' = to_date))"""
|
||||
|
||||
elif self.from_date and not self.to_date:
|
||||
conds += """ and to_date > '{from_date}'""".format(from_date=self.from_date)
|
||||
conds += f""" and to_date > '{self.from_date}'"""
|
||||
|
||||
elif self.to_date and not self.from_date:
|
||||
conds += """ and from_date < '{to_date}'""".format(to_date=self.to_date)
|
||||
conds += f""" and from_date < '{self.to_date}'"""
|
||||
|
||||
tax_rule = frappe.db.sql(
|
||||
"select name, priority \
|
||||
from `tabTax Rule` where {0} and name != '{1}'".format(
|
||||
conds, self.name
|
||||
),
|
||||
f"select name, priority \
|
||||
from `tabTax Rule` where {conds} and name != '{self.name}'",
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
@@ -189,27 +185,25 @@ def get_tax_template(posting_date, args):
|
||||
conditions.append("(from_date is null) and (to_date is null)")
|
||||
|
||||
conditions.append(
|
||||
"ifnull(tax_category, '') = {0}".format(frappe.db.escape(cstr(args.get("tax_category"))))
|
||||
"ifnull(tax_category, '') = {}".format(frappe.db.escape(cstr(args.get("tax_category"))))
|
||||
)
|
||||
if "tax_category" in args.keys():
|
||||
del args["tax_category"]
|
||||
|
||||
for key, value in args.items():
|
||||
if key == "use_for_shopping_cart":
|
||||
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
|
||||
conditions.append(f"use_for_shopping_cart = {1 if value else 0}")
|
||||
elif key == "customer_group":
|
||||
if not value:
|
||||
value = get_root_of("Customer Group")
|
||||
customer_group_condition = get_customer_group_condition(value)
|
||||
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
|
||||
conditions.append(f"ifnull({key}, '') in ('', {customer_group_condition})")
|
||||
else:
|
||||
conditions.append("ifnull({0}, '') in ('', {1})".format(key, frappe.db.escape(cstr(value))))
|
||||
conditions.append(f"ifnull({key}, '') in ('', {frappe.db.escape(cstr(value))})")
|
||||
|
||||
tax_rule = frappe.db.sql(
|
||||
"""select * from `tabTax Rule`
|
||||
where {0}""".format(
|
||||
" and ".join(conditions)
|
||||
),
|
||||
where {}""".format(" and ".join(conditions)),
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
@@ -234,7 +228,7 @@ def get_tax_template(posting_date, args):
|
||||
)[0]
|
||||
|
||||
tax_template = rule.sales_tax_template or rule.purchase_tax_template
|
||||
doctype = "{0} Taxes and Charges Template".format(rule.tax_type)
|
||||
doctype = f"{rule.tax_type} Taxes and Charges Template"
|
||||
|
||||
if frappe.db.get_value(doctype, tax_template, "disabled") == 1:
|
||||
return None
|
||||
@@ -244,9 +238,7 @@ def get_tax_template(posting_date, args):
|
||||
|
||||
def get_customer_group_condition(customer_group):
|
||||
condition = ""
|
||||
customer_groups = [
|
||||
"%s" % (frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)
|
||||
]
|
||||
customer_groups = ["%s" % (frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)]
|
||||
if customer_groups:
|
||||
condition = ",".join(["%s"] * len(customer_groups)) % (tuple(customer_groups))
|
||||
return condition
|
||||
|
||||
@@ -57,13 +57,11 @@ class TaxWithholdingCategory(Document):
|
||||
|
||||
def validate_thresholds(self):
|
||||
for d in self.get("rates"):
|
||||
if (
|
||||
d.cumulative_threshold and d.single_threshold and d.cumulative_threshold < d.single_threshold
|
||||
):
|
||||
if d.cumulative_threshold and d.single_threshold and d.cumulative_threshold < d.single_threshold:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold").format(
|
||||
d.idx
|
||||
)
|
||||
_(
|
||||
"Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold"
|
||||
).format(d.idx)
|
||||
)
|
||||
|
||||
|
||||
@@ -319,9 +317,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
||||
def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
||||
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
|
||||
field = (
|
||||
"base_tax_withholding_net_total as base_net_total"
|
||||
if party_type == "Supplier"
|
||||
else "base_net_total"
|
||||
"base_tax_withholding_net_total as base_net_total" if party_type == "Supplier" else "base_net_total"
|
||||
)
|
||||
voucher_wise_amount = {}
|
||||
vouchers = []
|
||||
@@ -375,9 +371,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
||||
return vouchers, voucher_wise_amount
|
||||
|
||||
|
||||
def get_advance_vouchers(
|
||||
parties, company=None, from_date=None, to_date=None, party_type="Supplier"
|
||||
):
|
||||
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
|
||||
"""
|
||||
Use Payment Ledger to fetch unallocated Advance Payments
|
||||
"""
|
||||
@@ -398,9 +392,7 @@ def get_advance_vouchers(
|
||||
if from_date and to_date:
|
||||
conditions.append(ple.posting_date[from_date:to_date])
|
||||
|
||||
advances = (
|
||||
qb.from_(ple).select(ple.voucher_no).distinct().where(Criterion.all(conditions)).run(as_list=1)
|
||||
)
|
||||
advances = qb.from_(ple).select(ple.voucher_no).distinct().where(Criterion.all(conditions)).run(as_list=1)
|
||||
if advances:
|
||||
advances = [x[0] for x in advances]
|
||||
|
||||
@@ -566,9 +558,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
|
||||
conditions.append(ple.voucher_no == ple.against_voucher_no)
|
||||
conditions.append(ple.company == inv.company)
|
||||
|
||||
advances = (
|
||||
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1)
|
||||
)
|
||||
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1)
|
||||
|
||||
advance_amt = (
|
||||
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0
|
||||
@@ -627,9 +617,7 @@ def get_limit_consumed(ldc, parties):
|
||||
return limit_consumed
|
||||
|
||||
|
||||
def get_lower_deduction_amount(
|
||||
current_amount, limit_consumed, certificate_limit, rate, tax_details
|
||||
):
|
||||
def get_lower_deduction_amount(current_amount, limit_consumed, certificate_limit, rate, tax_details):
|
||||
if certificate_limit - flt(limit_consumed) - flt(current_amount) >= 0:
|
||||
return current_amount * rate / 100
|
||||
else:
|
||||
@@ -641,9 +629,7 @@ def get_lower_deduction_amount(
|
||||
|
||||
def is_valid_certificate(ldc, posting_date, limit_consumed):
|
||||
available_amount = flt(ldc.certificate_limit) - flt(limit_consumed)
|
||||
if (
|
||||
getdate(ldc.valid_from) <= getdate(posting_date) <= getdate(ldc.valid_upto)
|
||||
) and available_amount > 0:
|
||||
if (getdate(ldc.valid_from) <= getdate(posting_date) <= getdate(ldc.valid_upto)) and available_amount > 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user