diff --git a/.flake8 b/.flake8
index 5735456ae7da..4b852abd7c62 100644
--- a/.flake8
+++ b/.flake8
@@ -29,6 +29,9 @@ ignore =
B950,
W191,
E124, # closing bracket, irritating while writing QB code
+ E131, # continuation line unaligned for hanging indent
+ E123, # closing bracket does not match indentation of opening bracket's line
+ E101, # ensured by use of black
max-line-length = 200
exclude=.github/helper/semgrep_rules
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 88049be32d42..e9cb6cf90310 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -23,3 +23,6 @@ b147b85e6ac19a9220cd1e2958a6ebd99373283a
# removing six compatibility layer
8fe5feb6a4372bf5f2dfaf65fca41bbcc25c8ce7
+
+# bulk format python code with black
+494bd9ef78313436f0424b918f200dab8fc7c20b
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index d05bbbec50a2..afabe43fec0c 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -4,7 +4,10 @@ on:
pull_request:
paths-ignore:
- '**.js'
+ - '**.css'
- '**.md'
+ - '**.html'
+ - '**.csv'
workflow_dispatch:
concurrency:
diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml
index 40f93651f4a8..69be7656a6ce 100644
--- a/.github/workflows/server-tests-mariadb.yml
+++ b/.github/workflows/server-tests-mariadb.yml
@@ -4,8 +4,10 @@ on:
pull_request:
paths-ignore:
- '**.js'
+ - '**.css'
- '**.md'
- '**.html'
+ - '**.csv'
push:
branches: [ develop ]
paths-ignore:
diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
deleted file mode 100644
index ab6a53b5d925..000000000000
--- a/.github/workflows/ui-tests.yml
+++ /dev/null
@@ -1,117 +0,0 @@
-name: UI
-
-on:
- pull_request:
- paths-ignore:
- - '**.md'
- workflow_dispatch:
-
-concurrency:
- group: ui-develop-${{ github.event.number }}
- cancel-in-progress: true
-
-jobs:
- test:
- runs-on: ubuntu-latest
- timeout-minutes: 60
-
- strategy:
- fail-fast: false
-
- name: UI Tests (Cypress)
-
- services:
- mysql:
- image: mariadb:10.3
- env:
- MYSQL_ALLOW_EMPTY_PASSWORD: YES
- ports:
- - 3306:3306
- options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
-
- steps:
- - name: Clone
- uses: actions/checkout@v2
-
- - name: Setup Python
- uses: actions/setup-python@v2
- with:
- python-version: 3.8
-
- - uses: actions/setup-node@v2
- with:
- node-version: 14
- check-latest: true
-
- - name: Add to Hosts
- run: |
- echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
-
- - name: Cache pip
- uses: actions/cache@v2
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
- restore-keys: |
- ${{ runner.os }}-pip-
- ${{ runner.os }}-
-
- - name: Cache node modules
- uses: actions/cache@v2
- env:
- cache-name: cache-node-modules
- with:
- path: ~/.npm
- key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
- restore-keys: |
- ${{ runner.os }}-build-${{ env.cache-name }}-
- ${{ runner.os }}-build-
- ${{ runner.os }}-
-
- - name: Get yarn cache directory path
- id: yarn-cache-dir-path
- run: echo "::set-output name=dir::$(yarn cache dir)"
-
- - uses: actions/cache@v2
- id: yarn-cache
- with:
- path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
- key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-yarn-
-
- - name: Cache cypress binary
- uses: actions/cache@v2
- with:
- path: ~/.cache
- key: ${{ runner.os }}-cypress-
- restore-keys: |
- ${{ runner.os }}-cypress-
- ${{ runner.os }}-
-
- - name: Install
- run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
- env:
- DB: mariadb
- TYPE: ui
-
- - name: Site Setup
- run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests
-
- - name: cypress pre-requisites
- run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile
-
-
- - name: Build Assets
- run: cd ~/frappe-bench/ && bench build
- env:
- CI: Yes
-
- - name: UI Tests
- run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless
- env:
- CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd
-
- - name: Show bench console if tests failed
- if: ${{ failure() }}
- run: cat ~/frappe-bench/bench_run_logs.txt
diff --git a/.mergify.yml b/.mergify.yml
index b7d1df4524f7..315d90febc62 100644
--- a/.mergify.yml
+++ b/.mergify.yml
@@ -7,6 +7,8 @@ pull_request_rules:
- author!=gavindsouza
- author!=rohitwaghchaure
- author!=nabinhait
+ - author!=ankush
+ - author!=deepeshgarg007
- or:
- base=version-13
- base=version-12
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b74d9a640da1..dc3011f050fb 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -26,12 +26,19 @@ repos:
args: ['--config', '.github/helper/.flake8_strict']
exclude: ".*setup.py$"
+ - repo: https://github.com/adityahase/black
+ rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
+ hooks:
+ - id: black
+ additional_dependencies: ['click==8.0.4']
+
- repo: https://github.com/timothycrosley/isort
rev: 5.9.1
hooks:
- id: isort
exclude: ".*setup.py$"
+
ci:
autoupdate_schedule: weekly
skip: []
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index dcfad1f100e1..e0f0c98e9c94 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -2,49 +2,57 @@
import frappe
-__version__ = '14.0.0-dev'
+__version__ = "14.0.0-dev"
+
def get_default_company(user=None):
- '''Get default company for user'''
+ """Get default company for user"""
from frappe.defaults import get_user_default_as_list
if not user:
user = frappe.session.user
- companies = get_user_default_as_list(user, 'company')
+ companies = get_user_default_as_list(user, "company")
if companies:
default_company = companies[0]
else:
- default_company = frappe.db.get_single_value('Global Defaults', 'default_company')
+ default_company = frappe.db.get_single_value("Global Defaults", "default_company")
return default_company
def get_default_currency():
- '''Returns the currency of the default company'''
+ """Returns the currency of the default company"""
company = get_default_company()
if company:
- return frappe.get_cached_value('Company', company, 'default_currency')
+ return frappe.get_cached_value("Company", company, "default_currency")
+
def get_default_cost_center(company):
- '''Returns the default cost center of the company'''
+ """Returns the default cost center of the company"""
if not company:
return None
if not frappe.flags.company_cost_center:
frappe.flags.company_cost_center = {}
if not company in frappe.flags.company_cost_center:
- frappe.flags.company_cost_center[company] = frappe.get_cached_value('Company', company, 'cost_center')
+ frappe.flags.company_cost_center[company] = frappe.get_cached_value(
+ "Company", company, "cost_center"
+ )
return frappe.flags.company_cost_center[company]
+
def get_company_currency(company):
- '''Returns the default company currency'''
+ """Returns the default company currency"""
if not frappe.flags.company_currency:
frappe.flags.company_currency = {}
if not company in frappe.flags.company_currency:
- frappe.flags.company_currency[company] = frappe.db.get_value('Company', company, 'default_currency', cache=True)
+ frappe.flags.company_currency[company] = frappe.db.get_value(
+ "Company", company, "default_currency", cache=True
+ )
return frappe.flags.company_currency[company]
+
def set_perpetual_inventory(enable=1, company=None):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
@@ -53,9 +61,10 @@ def set_perpetual_inventory(enable=1, company=None):
company.enable_perpetual_inventory = enable
company.save()
+
def encode_company_abbr(name, company=None, abbr=None):
- '''Returns name encoded with company abbreviation'''
- company_abbr = abbr or frappe.get_cached_value('Company', company, "abbr")
+ """Returns name encoded with company abbreviation"""
+ company_abbr = abbr or frappe.get_cached_value("Company", company, "abbr")
parts = name.rsplit(" - ", 1)
if parts[-1].lower() != company_abbr.lower():
@@ -63,62 +72,69 @@ def encode_company_abbr(name, company=None, abbr=None):
return " - ".join(parts)
+
def is_perpetual_inventory_enabled(company):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
- if not hasattr(frappe.local, 'enable_perpetual_inventory'):
+ if not hasattr(frappe.local, "enable_perpetual_inventory"):
frappe.local.enable_perpetual_inventory = {}
if not company in frappe.local.enable_perpetual_inventory:
- frappe.local.enable_perpetual_inventory[company] = frappe.get_cached_value('Company',
- company, "enable_perpetual_inventory") or 0
+ frappe.local.enable_perpetual_inventory[company] = (
+ frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
+ )
return frappe.local.enable_perpetual_inventory[company]
+
def get_default_finance_book(company=None):
if not company:
company = get_default_company()
- if not hasattr(frappe.local, 'default_finance_book'):
+ if not hasattr(frappe.local, "default_finance_book"):
frappe.local.default_finance_book = {}
if not company in frappe.local.default_finance_book:
- frappe.local.default_finance_book[company] = frappe.get_cached_value('Company',
- company, "default_finance_book")
+ frappe.local.default_finance_book[company] = frappe.get_cached_value(
+ "Company", company, "default_finance_book"
+ )
return frappe.local.default_finance_book[company]
+
def get_party_account_type(party_type):
- if not hasattr(frappe.local, 'party_account_types'):
+ if not hasattr(frappe.local, "party_account_types"):
frappe.local.party_account_types = {}
if not party_type in frappe.local.party_account_types:
- frappe.local.party_account_types[party_type] = frappe.db.get_value("Party Type",
- party_type, "account_type") or ''
+ frappe.local.party_account_types[party_type] = (
+ frappe.db.get_value("Party Type", party_type, "account_type") or ""
+ )
return frappe.local.party_account_types[party_type]
+
def get_region(company=None):
- '''Return the default country based on flag, company or global settings
+ """Return the default country based on flag, company or global settings
You can also set global company flag in `frappe.flags.company`
- '''
+ """
if company or frappe.flags.company:
- return frappe.get_cached_value('Company',
- company or frappe.flags.company, 'country')
+ return frappe.get_cached_value("Company", company or frappe.flags.company, "country")
elif frappe.flags.country:
return frappe.flags.country
else:
- return frappe.get_system_settings('country')
+ return frappe.get_system_settings("country")
+
def allow_regional(fn):
- '''Decorator to make a function regionally overridable
+ """Decorator to make a function regionally overridable
Example:
@erpnext.allow_regional
def myfunction():
- pass'''
+ pass"""
def caller(*args, **kwargs):
overrides = frappe.get_hooks("regional_overrides", {}).get(get_region())
diff --git a/erpnext/accounts/custom/address.py b/erpnext/accounts/custom/address.py
index 551048e50b47..775a81fd25f3 100644
--- a/erpnext/accounts/custom/address.py
+++ b/erpnext/accounts/custom/address.py
@@ -21,37 +21,39 @@ def link_address(self):
return super(ERPNextAddress, self).link_address()
def update_compnay_address(self):
- for link in self.get('links'):
- if link.link_doctype == 'Company':
+ for link in self.get("links"):
+ if link.link_doctype == "Company":
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"
]:
- frappe.throw(_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
- title=_("Company Not Linked"))
+ frappe.throw(
+ _("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
+ title=_("Company Not Linked"),
+ )
def on_update(self):
"""
After Address is updated, update the related 'Primary Address' on Customer.
"""
address_display = get_address_display(self.as_dict())
- filters = { "customer_primary_address": self.name }
+ filters = {"customer_primary_address": self.name}
customers = frappe.db.get_all("Customer", filters=filters, as_list=True)
for customer_name in customers:
frappe.db.set_value("Customer", customer_name[0], "primary_address", address_display)
+
@frappe.whitelist()
-def get_shipping_address(company, address = None):
+def get_shipping_address(company, address=None):
filters = [
["Dynamic Link", "link_doctype", "=", "Company"],
["Dynamic Link", "link_name", "=", company],
- ["Address", "is_your_company_address", "=", 1]
+ ["Address", "is_your_company_address", "=", 1],
]
fields = ["*"]
- if address and frappe.db.get_value('Dynamic Link',
- {'parent': address, 'link_name': company}):
+ if address and frappe.db.get_value("Dynamic Link", {"parent": address, "link_name": company}):
filters.append(["Address", "name", "=", address])
if not address:
filters.append(["Address", "is_shipping_address", "=", 1])
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
index 1c1364ed111a..fefec0ee7b41 100644
--- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
@@ -12,15 +12,24 @@
@frappe.whitelist()
@cache_source
-def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
- to_date = None, timespan = None, time_interval = None, heatmap_year = None):
+def get(
+ chart_name=None,
+ chart=None,
+ no_cache=None,
+ filters=None,
+ from_date=None,
+ to_date=None,
+ timespan=None,
+ time_interval=None,
+ heatmap_year=None,
+):
if chart_name:
- chart = frappe.get_doc('Dashboard Chart', chart_name)
+ chart = frappe.get_doc("Dashboard Chart", chart_name)
else:
chart = frappe._dict(frappe.parse_json(chart))
timespan = chart.timespan
- if chart.timespan == 'Select Date Range':
+ if chart.timespan == "Select Date Range":
from_date = chart.from_date
to_date = chart.to_date
@@ -31,17 +40,23 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
company = filters.get("company")
if not account and chart_name:
- frappe.throw(_("Account is not set for the dashboard chart {0}")
- .format(get_link_to_form("Dashboard Chart", chart_name)))
+ frappe.throw(
+ _("Account is not set for the dashboard chart {0}").format(
+ get_link_to_form("Dashboard Chart", chart_name)
+ )
+ )
if not frappe.db.exists("Account", account) and chart_name:
- frappe.throw(_("Account {0} does not exists in the dashboard chart {1}")
- .format(account, get_link_to_form("Dashboard Chart", chart_name)))
+ frappe.throw(
+ _("Account {0} does not exists in the dashboard chart {1}").format(
+ account, get_link_to_form("Dashboard Chart", chart_name)
+ )
+ )
if not to_date:
to_date = nowdate()
if not from_date:
- if timegrain in ('Monthly', 'Quarterly'):
+ if timegrain in ("Monthly", "Quarterly"):
from_date = get_from_date_from_timespan(to_date, timespan)
# fetch dates to plot
@@ -54,16 +69,14 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
result = build_result(account, dates, gl_entries)
return {
- "labels": [formatdate(r[0].strftime('%Y-%m-%d')) for r in result],
- "datasets": [{
- "name": account,
- "values": [r[1] for r in result]
- }]
+ "labels": [formatdate(r[0].strftime("%Y-%m-%d")) for r in result],
+ "datasets": [{"name": account, "values": [r[1] for r in result]}],
}
+
def build_result(account, dates, gl_entries):
result = [[getdate(date), 0.0] for date in dates]
- root_type = frappe.db.get_value('Account', account, 'root_type')
+ root_type = frappe.db.get_value("Account", account, "root_type")
# start with the first date
date_index = 0
@@ -78,30 +91,34 @@ def build_result(account, dates, gl_entries):
result[date_index][1] += entry.debit - entry.credit
# if account type is credit, switch balances
- if root_type not in ('Asset', 'Expense'):
+ if root_type not in ("Asset", "Expense"):
for r in result:
r[1] = -1 * r[1]
# for balance sheet accounts, the totals are cumulative
- if root_type in ('Asset', 'Liability', 'Equity'):
+ if root_type in ("Asset", "Liability", "Equity"):
for i, r in enumerate(result):
if i > 0:
- r[1] = r[1] + result[i-1][1]
+ r[1] = r[1] + result[i - 1][1]
return result
+
def get_gl_entries(account, to_date):
- child_accounts = get_descendants_of('Account', account, ignore_permissions=True)
+ child_accounts = get_descendants_of("Account", account, ignore_permissions=True)
child_accounts.append(account)
- return frappe.db.get_all('GL Entry',
- fields = ['posting_date', 'debit', 'credit'],
- filters = [
- dict(posting_date = ('<', to_date)),
- dict(account = ('in', child_accounts)),
- dict(voucher_type = ('!=', 'Period Closing Voucher'))
+ return frappe.db.get_all(
+ "GL Entry",
+ fields=["posting_date", "debit", "credit"],
+ filters=[
+ dict(posting_date=("<", to_date)),
+ dict(account=("in", child_accounts)),
+ dict(voucher_type=("!=", "Period Closing Voucher")),
],
- order_by = 'posting_date asc')
+ order_by="posting_date asc",
+ )
+
def get_dates_from_timegrain(from_date, to_date, timegrain):
days = months = years = 0
@@ -116,6 +133,8 @@ 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
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index ab1061beeb34..0611f880c5ec 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -22,20 +22,23 @@
def validate_service_stop_date(doc):
- ''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
+ """Validates service_stop_date for Purchase Invoice and Sales Invoice"""
- enable_check = "enable_deferred_revenue" \
- if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
+ enable_check = (
+ "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
+ )
old_stop_dates = {}
- old_doc = frappe.db.get_all("{0} Item".format(doc.doctype),
- {"parent": doc.name}, ["name", "service_stop_date"])
+ old_doc = frappe.db.get_all(
+ "{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
+ )
for d in old_doc:
old_stop_dates[d.name] = d.service_stop_date or ""
for item in doc.items:
- if not item.get(enable_check): continue
+ if not item.get(enable_check):
+ continue
if item.service_stop_date:
if date_diff(item.service_stop_date, item.service_start_date) < 0:
@@ -44,21 +47,31 @@ def validate_service_stop_date(doc):
if date_diff(item.service_stop_date, item.service_end_date) > 0:
frappe.throw(_("Service Stop Date cannot be after Service End Date"))
- if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates.get(item.name):
+ if (
+ old_stop_dates
+ and old_stop_dates.get(item.name)
+ and item.service_stop_date != old_stop_dates.get(item.name)
+ ):
frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx))
+
def build_conditions(process_type, account, company):
- conditions=''
- deferred_account = "item.deferred_revenue_account" if process_type=="Income" else "item.deferred_expense_account"
+ conditions = ""
+ deferred_account = (
+ "item.deferred_revenue_account" if process_type == "Income" else "item.deferred_expense_account"
+ )
if account:
- conditions += "AND %s='%s'"%(deferred_account, account)
+ conditions += "AND %s='%s'" % (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:
@@ -67,14 +80,19 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
end_date = add_days(today(), -1)
# check for the purchase invoice for which GL entries has to be done
- invoices = frappe.db.sql_list('''
+ invoices = frappe.db.sql_list(
+ """
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), (end_date, start_date)) #nosec
+ """.format(
+ conditions
+ ),
+ (end_date, start_date),
+ ) # nosec
# For each invoice, book deferred expense
for invoice in invoices:
@@ -84,7 +102,10 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
if frappe.flags.deferred_accounting_error:
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:
@@ -93,14 +114,19 @@ def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_da
end_date = add_days(today(), -1)
# check for the sales invoice for which GL entries has to be done
- invoices = frappe.db.sql_list('''
+ invoices = frappe.db.sql_list(
+ """
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), (end_date, start_date)) #nosec
+ """.format(
+ conditions
+ ),
+ (end_date, start_date),
+ ) # nosec
for invoice in invoices:
doc = frappe.get_doc("Sales Invoice", invoice)
@@ -109,31 +135,43 @@ def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_da
if frappe.flags.deferred_accounting_error:
send_mail(deferred_process)
+
def get_booking_dates(doc, item, posting_date=None):
if not posting_date:
posting_date = add_days(today(), -1)
last_gl_entry = False
- deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
+ deferred_account = (
+ "deferred_revenue_account" if doc.doctype == "Sales Invoice" else "deferred_expense_account"
+ )
- prev_gl_entry = frappe.db.sql('''
+ prev_gl_entry = frappe.db.sql(
+ """
select name, posting_date 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
order by posting_date desc limit 1
- ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+ """,
+ (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
+ as_dict=True,
+ )
- prev_gl_via_je = frappe.db.sql('''
+ prev_gl_via_je = frappe.db.sql(
+ """
SELECT p.name, p.posting_date 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 c.docstatus < 2 order by posting_date desc limit 1
- ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+ """,
+ (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
+ as_dict=True,
+ )
if prev_gl_via_je:
- if (not prev_gl_entry) or (prev_gl_entry and
- prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date):
+ if (not prev_gl_entry) or (
+ prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date
+ ):
prev_gl_entry = prev_gl_via_je
if prev_gl_entry:
@@ -157,66 +195,94 @@ def get_booking_dates(doc, item, posting_date=None):
else:
return None, None, None
-def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
+
+def calculate_monthly_amount(
+ doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency
+):
amount, base_amount = 0, 0
if not last_gl_entry:
- total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \
- (item.service_end_date.month - item.service_start_date.month) + 1
+ total_months = (
+ (item.service_end_date.year - item.service_start_date.year) * 12
+ + (item.service_end_date.month - item.service_start_date.month)
+ + 1
+ )
- prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \
- / flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date)))
+ prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) / flt(
+ date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date))
+ )
actual_months = rounded(total_months * prorate_factor, 1)
- already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
+ already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
+ doc, item
+ )
base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount"))
if base_amount + already_booked_amount > item.base_net_amount:
base_amount = item.base_net_amount - already_booked_amount
- if account_currency==doc.company_currency:
+ if account_currency == doc.company_currency:
amount = base_amount
else:
- amount = flt(item.net_amount/actual_months, item.precision("net_amount"))
+ amount = flt(item.net_amount / actual_months, item.precision("net_amount"))
if amount + already_booked_amount_in_account_currency > item.net_amount:
amount = item.net_amount - already_booked_amount_in_account_currency
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
- partial_month = flt(date_diff(end_date, start_date)) \
- / flt(date_diff(get_last_day(end_date), get_first_day(start_date)))
+ partial_month = flt(date_diff(end_date, start_date)) / flt(
+ date_diff(get_last_day(end_date), get_first_day(start_date))
+ )
base_amount = rounded(partial_month, 1) * base_amount
amount = rounded(partial_month, 1) * 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"))
- if account_currency==doc.company_currency:
+ 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")
+ )
+ if account_currency == doc.company_currency:
amount = base_amount
else:
- amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
+ amount = flt(
+ item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")
+ )
return amount, base_amount
+
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
amount, base_amount = 0, 0
if not last_gl_entry:
- base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
- if account_currency==doc.company_currency:
+ base_amount = flt(
+ item.base_net_amount * total_booking_days / flt(total_days), item.precision("base_net_amount")
+ )
+ 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"))
- if account_currency==doc.company_currency:
+ 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")
+ )
+ if account_currency == doc.company_currency:
amount = base_amount
else:
- amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
+ amount = flt(
+ item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")
+ )
return amount, base_amount
+
def get_already_booked_amount(doc, item):
if doc.doctype == "Sales Invoice":
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
@@ -225,21 +291,31 @@ def get_already_booked_amount(doc, item):
total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
deferred_account = "deferred_expense_account"
- gl_entries_details = frappe.db.sql('''
+ gl_entries_details = frappe.db.sql(
+ """
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0
group by voucher_detail_no
- '''.format(total_credit_debit, total_credit_debit_currency),
- (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+ """.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('''
+ 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
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),
- (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+ """.format(
+ total_credit_debit, total_credit_debit_currency
+ ),
+ (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
+ as_dict=True,
+ )
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0
@@ -247,20 +323,29 @@ def get_already_booked_amount(doc, item):
if doc.currency == doc.company_currency:
already_booked_amount_in_account_currency = already_booked_amount
else:
- already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
- already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
+ already_booked_amount_in_account_currency = (
+ gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
+ )
+ already_booked_amount_in_account_currency += (
+ journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
+ )
return already_booked_amount, already_booked_amount_in_account_currency
+
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.get_cached_value('Accounts Settings', 'None', 'acc_frozen_upto')
+ accounts_frozen_upto = frappe.get_cached_value("Accounts Settings", "None", "acc_frozen_upto")
- def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
+ def _book_deferred_revenue_or_expense(
+ item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
+ ):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
- if not (start_date and end_date): return
+ if not (start_date and end_date):
+ return
account_currency = get_account_currency(item.expense_account or item.income_account)
if doc.doctype == "Sales Invoice":
@@ -273,12 +358,21 @@ def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_en
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(end_date, start_date) + 1
- if book_deferred_entries_based_on == 'Months':
- amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry,
- start_date, end_date, total_days, total_booking_days, account_currency)
+ if book_deferred_entries_based_on == "Months":
+ amount, base_amount = calculate_monthly_amount(
+ doc,
+ item,
+ last_gl_entry,
+ start_date,
+ end_date,
+ total_days,
+ total_booking_days,
+ account_currency,
+ )
else:
- amount, base_amount = calculate_amount(doc, item, last_gl_entry,
- total_days, total_booking_days, account_currency)
+ amount, base_amount = calculate_amount(
+ doc, item, last_gl_entry, total_days, total_booking_days, account_currency
+ )
if not amount:
return
@@ -288,92 +382,156 @@ def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_en
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
if via_journal_entry:
- book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
- base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
+ book_revenue_via_journal_entry(
+ doc,
+ credit_account,
+ debit_account,
+ against,
+ amount,
+ base_amount,
+ end_date,
+ project,
+ account_currency,
+ item.cost_center,
+ item,
+ deferred_process,
+ submit_journal_entry,
+ )
else:
- make_gl_entries(doc, credit_account, debit_account, against,
- amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
+ make_gl_entries(
+ doc,
+ credit_account,
+ debit_account,
+ against,
+ amount,
+ base_amount,
+ end_date,
+ project,
+ account_currency,
+ item.cost_center,
+ item,
+ deferred_process,
+ )
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error:
return
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
- _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
+ _book_deferred_revenue_or_expense(
+ item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
+ )
- 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'))
- book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on')
+ 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")
+ )
+ book_deferred_entries_based_on = frappe.db.get_singles_value(
+ "Accounts Settings", "book_deferred_entries_based_on"
+ )
- for item in doc.get('items'):
+ for item in doc.get("items"):
if item.get(enable_check):
- _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
+ _book_deferred_revenue_or_expense(
+ item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
+ )
+
def process_deferred_accounting(posting_date=None):
- ''' Converts deferred income/expense into income/expense
- Executed via background jobs on every month end '''
+ """Converts deferred income/expense into income/expense
+ Executed via background jobs on every month end"""
if not posting_date:
posting_date = today()
- if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')):
+ if not cint(
+ frappe.db.get_singles_value(
+ "Accounts Settings", "automatically_process_deferred_accounting_entry"
+ )
+ ):
return
start_date = add_months(today(), -1)
end_date = add_days(today(), -1)
- companies = frappe.get_all('Company')
+ companies = frappe.get_all("Company")
for company in companies:
- for record_type in ('Income', 'Expense'):
- doc = frappe.get_doc(dict(
- doctype='Process Deferred Accounting',
- company=company.name,
- posting_date=posting_date,
- start_date=start_date,
- end_date=end_date,
- type=record_type
- ))
+ for record_type in ("Income", "Expense"):
+ doc = frappe.get_doc(
+ dict(
+ doctype="Process Deferred Accounting",
+ company=company.name,
+ posting_date=posting_date,
+ start_date=start_date,
+ end_date=end_date,
+ type=record_type,
+ )
+ )
doc.insert()
doc.submit()
-def make_gl_entries(doc, credit_account, debit_account, against,
- amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
+
+def make_gl_entries(
+ doc,
+ credit_account,
+ debit_account,
+ against,
+ amount,
+ base_amount,
+ posting_date,
+ project,
+ account_currency,
+ cost_center,
+ item,
+ deferred_process=None,
+):
# GL Entry for crediting the amount in the deferred expense
from erpnext.accounts.general_ledger import make_gl_entries
- if amount == 0: return
+ if amount == 0:
+ return
gl_entries = []
gl_entries.append(
- doc.get_gl_dict({
- "account": credit_account,
- "against": against,
- "credit": base_amount,
- "credit_in_account_currency": amount,
- "cost_center": cost_center,
- "voucher_detail_no": item.name,
- 'posting_date': posting_date,
- 'project': project,
- 'against_voucher_type': 'Process Deferred Accounting',
- 'against_voucher': deferred_process
- }, account_currency, item=item)
+ doc.get_gl_dict(
+ {
+ "account": credit_account,
+ "against": against,
+ "credit": base_amount,
+ "credit_in_account_currency": amount,
+ "cost_center": cost_center,
+ "voucher_detail_no": item.name,
+ "posting_date": posting_date,
+ "project": project,
+ "against_voucher_type": "Process Deferred Accounting",
+ "against_voucher": deferred_process,
+ },
+ account_currency,
+ item=item,
+ )
)
# GL Entry to debit the amount from the expense
gl_entries.append(
- doc.get_gl_dict({
- "account": debit_account,
- "against": against,
- "debit": base_amount,
- "debit_in_account_currency": amount,
- "cost_center": cost_center,
- "voucher_detail_no": item.name,
- 'posting_date': posting_date,
- 'project': project,
- 'against_voucher_type': 'Process Deferred Accounting',
- 'against_voucher': deferred_process
- }, account_currency, item=item)
+ doc.get_gl_dict(
+ {
+ "account": debit_account,
+ "against": against,
+ "debit": base_amount,
+ "debit_in_account_currency": amount,
+ "cost_center": cost_center,
+ "voucher_detail_no": item.name,
+ "posting_date": posting_date,
+ "project": project,
+ "against_voucher_type": "Process Deferred Accounting",
+ "against_voucher": deferred_process,
+ },
+ account_currency,
+ item=item,
+ )
)
if gl_entries:
@@ -383,68 +541,88 @@ def make_gl_entries(doc, credit_account, debit_account, against,
except Exception as e:
if frappe.flags.in_test:
traceback = frappe.get_traceback()
- frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
+ frappe.log_error(
+ title=_("Error while processing deferred accounting for Invoice {0}").format(doc.name),
+ message=traceback,
+ )
raise e
else:
frappe.db.rollback()
traceback = frappe.get_traceback()
- frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
+ frappe.log_error(
+ title=_("Error while processing deferred accounting for Invoice {0}").format(doc.name),
+ message=traceback,
+ )
frappe.flags.deferred_accounting_error = True
+
def send_mail(deferred_process):
title = _("Error while processing deferred accounting for {0}").format(deferred_process)
- link = get_link_to_form('Process Deferred Accounting', deferred_process)
+ link = get_link_to_form("Process Deferred Accounting", deferred_process)
content = _("Deferred accounting failed for some invoices:") + "\n"
- content += _("Please check Process Deferred Accounting {0} and submit manually after resolving errors.").format(link)
+ content += _(
+ "Please check Process Deferred Accounting {0} and submit manually after resolving errors."
+ ).format(link)
sendmail_to_system_managers(title, content)
-def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
- amount, base_amount, posting_date, project, account_currency, cost_center, item,
- deferred_process=None, submit='No'):
- if amount == 0: return
+def book_revenue_via_journal_entry(
+ doc,
+ credit_account,
+ debit_account,
+ against,
+ amount,
+ base_amount,
+ posting_date,
+ project,
+ account_currency,
+ cost_center,
+ item,
+ deferred_process=None,
+ submit="No",
+):
+
+ if amount == 0:
+ return
- journal_entry = frappe.new_doc('Journal Entry')
+ 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"
+ )
debit_entry = {
- 'account': credit_account,
- 'credit': base_amount,
- 'credit_in_account_currency': amount,
- 'account_currency': account_currency,
- 'reference_name': doc.name,
- 'reference_type': doc.doctype,
- 'reference_detail_no': item.name,
- 'cost_center': cost_center,
- 'project': project,
+ "account": credit_account,
+ "credit": base_amount,
+ "credit_in_account_currency": amount,
+ "account_currency": account_currency,
+ "reference_name": doc.name,
+ "reference_type": doc.doctype,
+ "reference_detail_no": item.name,
+ "cost_center": cost_center,
+ "project": project,
}
credit_entry = {
- 'account': debit_account,
- 'debit': base_amount,
- 'debit_in_account_currency': amount,
- 'account_currency': account_currency,
- 'reference_name': doc.name,
- 'reference_type': doc.doctype,
- 'reference_detail_no': item.name,
- 'cost_center': cost_center,
- 'project': project,
+ "account": debit_account,
+ "debit": base_amount,
+ "debit_in_account_currency": amount,
+ "account_currency": account_currency,
+ "reference_name": doc.name,
+ "reference_type": doc.doctype,
+ "reference_detail_no": item.name,
+ "cost_center": cost_center,
+ "project": project,
}
for dimension in get_accounting_dimensions():
- debit_entry.update({
- dimension: item.get(dimension)
- })
+ debit_entry.update({dimension: item.get(dimension)})
- credit_entry.update({
- dimension: item.get(dimension)
- })
+ credit_entry.update({dimension: item.get(dimension)})
- journal_entry.append('accounts', debit_entry)
- journal_entry.append('accounts', credit_entry)
+ journal_entry.append("accounts", debit_entry)
+ journal_entry.append("accounts", credit_entry)
try:
journal_entry.save()
@@ -456,20 +634,30 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
except Exception:
frappe.db.rollback()
traceback = frappe.get_traceback()
- frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
+ frappe.log_error(
+ title=_("Error while processing deferred accounting for Invoice {0}").format(doc.name),
+ message=traceback,
+ )
frappe.flags.deferred_accounting_error = True
+
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', {'name': voucher_detail_no},
- ['income_account', 'deferred_revenue_account'])
+ if doctype == "Sales Invoice":
+ credit_account, debit_account = frappe.db.get_value(
+ "Sales Invoice Item",
+ {"name": voucher_detail_no},
+ ["income_account", "deferred_revenue_account"],
+ )
else:
- credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no},
- ['deferred_expense_account', 'expense_account'])
+ credit_account, debit_account = frappe.db.get_value(
+ "Purchase Invoice Item",
+ {"name": voucher_detail_no},
+ ["deferred_expense_account", "expense_account"],
+ )
- if dr_or_cr == 'Debit':
+ if dr_or_cr == "Debit":
return debit_account
else:
return credit_account
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index f8a06c7243f7..c71ea3648b99 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -10,11 +10,17 @@
import erpnext
-class RootNotEditable(frappe.ValidationError): pass
-class BalanceMismatchError(frappe.ValidationError): pass
+class RootNotEditable(frappe.ValidationError):
+ pass
+
+
+class BalanceMismatchError(frappe.ValidationError):
+ pass
+
class Account(NestedSet):
- nsm_parent_field = 'parent_account'
+ nsm_parent_field = "parent_account"
+
def on_update(self):
if frappe.local.flags.ignore_update_nsm:
return
@@ -22,17 +28,20 @@ def on_update(self):
super(Account, self).on_update()
def onload(self):
- frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", "Accounts Settings",
- "frozen_accounts_modifier")
+ frozen_accounts_modifier = frappe.db.get_value(
+ "Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
+ )
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
self.set_onload("can_freeze_account", True)
def autoname(self):
from erpnext.accounts.utils import get_autoname_with_number
+
self.name = get_autoname_with_number(self.account_number, self.account_name, None, self.company)
def validate(self):
from erpnext.accounts.utils import validate_field_number
+
if frappe.local.flags.allow_unverified_charts:
return
self.validate_parent()
@@ -49,22 +58,33 @@ def validate(self):
def validate_parent(self):
"""Fetch Parent Details and validate parent account"""
if self.parent_account:
- par = frappe.db.get_value("Account", self.parent_account,
- ["name", "is_group", "company"], as_dict=1)
+ par = frappe.db.get_value(
+ "Account", self.parent_account, ["name", "is_group", "company"], as_dict=1
+ )
if not par:
- throw(_("Account {0}: Parent account {1} does not exist").format(self.name, self.parent_account))
+ throw(
+ _("Account {0}: Parent account {1} does not exist").format(self.name, self.parent_account)
+ )
elif par.name == self.name:
throw(_("Account {0}: You can not assign itself as parent account").format(self.name))
elif not par.is_group:
- throw(_("Account {0}: Parent account {1} can not be a ledger").format(self.name, self.parent_account))
+ throw(
+ _("Account {0}: Parent account {1} can not be a ledger").format(
+ self.name, self.parent_account
+ )
+ )
elif par.company != self.company:
- throw(_("Account {0}: Parent account {1} does not belong to company: {2}")
- .format(self.name, self.parent_account, self.company))
+ throw(
+ _("Account {0}: Parent account {1} does not belong to company: {2}").format(
+ self.name, self.parent_account, self.company
+ )
+ )
def set_root_and_report_type(self):
if self.parent_account:
- par = frappe.db.get_value("Account", self.parent_account,
- ["report_type", "root_type"], as_dict=1)
+ par = frappe.db.get_value(
+ "Account", self.parent_account, ["report_type", "root_type"], as_dict=1
+ )
if par.report_type:
self.report_type = par.report_type
@@ -75,15 +95,20 @@ def set_root_and_report_type(self):
db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
if db_value:
if self.report_type != db_value.report_type:
- frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
- (self.report_type, self.lft, self.rgt))
+ frappe.db.sql(
+ "update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
+ (self.report_type, self.lft, self.rgt),
+ )
if self.root_type != db_value.root_type:
- frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
- (self.root_type, self.lft, self.rgt))
+ frappe.db.sql(
+ "update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
+ (self.root_type, self.lft, self.rgt),
+ )
if self.root_type and not self.report_type:
- self.report_type = "Balance Sheet" \
- if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
+ self.report_type = (
+ "Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
+ )
def validate_root_details(self):
# does not exists parent
@@ -96,21 +121,26 @@ def validate_root_details(self):
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:
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
return
- if not frappe.db.get_value("Account",
- {'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
+ if not frappe.db.get_value(
+ "Account", {"account_name": self.account_name, "company": ancestors[0]}, "name"
+ ):
frappe.throw(_("Please add the account to root level Company - {}").format(ancestors[0]))
elif self.parent_account:
- descendants = get_descendants_of('Company', self.company)
- if not descendants: return
+ descendants = get_descendants_of("Company", self.company)
+ if not descendants:
+ return
parent_acc_name_map = {}
- parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \
- ["account_name", "account_number"])
+ parent_acc_name, parent_acc_number = frappe.db.get_value(
+ "Account", self.parent_account, ["account_name", "account_number"]
+ )
filters = {
"company": ["in", descendants],
"account_name": parent_acc_name,
@@ -118,10 +148,13 @@ def validate_root_company_and_sync_account_to_children(self):
if parent_acc_number:
filters["account_number"] = parent_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
+ ):
parent_acc_name_map[d["company"]] = d["name"]
- if not parent_acc_name_map: return
+ if not parent_acc_name_map:
+ return
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
@@ -142,26 +175,38 @@ def validate_group_or_ledger(self):
def validate_frozen_accounts_modifier(self):
old_value = frappe.db.get_value("Account", self.name, "freeze_account")
if old_value and old_value != self.freeze_account:
- frozen_accounts_modifier = frappe.db.get_value('Accounts Settings', None, 'frozen_accounts_modifier')
- if not frozen_accounts_modifier or \
- frozen_accounts_modifier not in frappe.get_roles():
- throw(_("You are not authorized to set Frozen value"))
+ frozen_accounts_modifier = frappe.db.get_value(
+ "Accounts Settings", None, "frozen_accounts_modifier"
+ )
+ if not frozen_accounts_modifier or frozen_accounts_modifier not in frappe.get_roles():
+ throw(_("You are not authorized to set Frozen value"))
def validate_balance_must_be_debit_or_credit(self):
from erpnext.accounts.utils import get_balance_on
+
if not self.get("__islocal") and self.balance_must_be:
account_balance = get_balance_on(self.name)
if account_balance > 0 and self.balance_must_be == "Credit":
- frappe.throw(_("Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"))
+ frappe.throw(
+ _(
+ "Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"
+ )
+ )
elif account_balance < 0 and self.balance_must_be == "Debit":
- frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"))
+ frappe.throw(
+ _(
+ "Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"
+ )
+ )
def validate_account_currency(self):
if not self.account_currency:
- self.account_currency = frappe.get_cached_value('Company', self.company, "default_currency")
+ self.account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
- elif self.account_currency != frappe.db.get_value("Account", self.name, "account_currency"):
+ gl_currency = frappe.db.get_value("GL Entry", {"account": self.name}, "account_currency")
+
+ if gl_currency and self.account_currency != gl_currency:
if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
@@ -170,45 +215,52 @@ def create_account_for_child_company(self, parent_acc_name_map, descendants, par
company_bold = frappe.bold(company)
parent_acc_name_bold = frappe.bold(parent_acc_name)
if not parent_acc_name_map.get(company):
- frappe.throw(_("While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
- .format(company_bold, parent_acc_name_bold), title=_("Account Not Found"))
+ frappe.throw(
+ _(
+ "While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA"
+ ).format(company_bold, parent_acc_name_bold),
+ title=_("Account Not Found"),
+ )
# validate if parent of child company account to be added is a group
- if (frappe.db.get_value("Account", self.parent_account, "is_group")
- and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")):
- msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold)
+ if frappe.db.get_value("Account", self.parent_account, "is_group") and not frappe.db.get_value(
+ "Account", parent_acc_name_map[company], "is_group"
+ ):
+ msg = _(
+ "While creating account for Child Company {0}, parent account {1} found as a ledger account."
+ ).format(company_bold, parent_acc_name_bold)
msg += "
"
- msg += _("Please convert the parent account in corresponding child company to a group account.")
+ msg += _(
+ "Please convert the parent account in corresponding child company to a group account."
+ )
frappe.throw(msg, title=_("Invalid Parent Account"))
- filters = {
- "account_name": self.account_name,
- "company": company
- }
+ filters = {"account_name": self.account_name, "company": company}
if self.account_number:
filters["account_number"] = self.account_number
- child_account = frappe.db.get_value("Account", filters, 'name')
+ child_account = frappe.db.get_value("Account", filters, "name")
if not child_account:
doc = frappe.copy_doc(self)
doc.flags.ignore_root_company_validation = True
- doc.update({
- "company": company,
- # parent account's currency should be passed down to child account's curreny
- # if it is None, it picks it up from default company currency, which might be unintended
- "account_currency": erpnext.get_company_currency(company),
- "parent_account": parent_acc_name_map[company]
- })
+ doc.update(
+ {
+ "company": company,
+ # parent account's currency should be passed down to child account's curreny
+ # if it is None, it picks it up from default company currency, which might be unintended
+ "account_currency": erpnext.get_company_currency(company),
+ "parent_account": parent_acc_name_map[company],
+ }
+ )
doc.save()
- frappe.msgprint(_("Account {0} is added in the child company {1}")
- .format(doc.name, company))
+ frappe.msgprint(_("Account {0} is added in the child company {1}").format(doc.name, company))
elif child_account:
# update the parent company's value in child companies
doc = frappe.get_doc("Account", child_account)
parent_value_changed = False
- for field in ['account_type', 'freeze_account', 'balance_must_be']:
+ for field in ["account_type", "freeze_account", "balance_must_be"]:
if doc.get(field) != self.get(field):
parent_value_changed = True
doc.set(field, self.get(field))
@@ -243,8 +295,11 @@ def check_gle_exists(self):
return frappe.db.get_value("GL Entry", {"account": self.name})
def check_if_child_exists(self):
- return frappe.db.sql("""select name from `tabAccount` where parent_account = %s
- and docstatus != 2""", self.name)
+ return frappe.db.sql(
+ """select name from `tabAccount` where parent_account = %s
+ and docstatus != 2""",
+ self.name,
+ )
def validate_mandatory(self):
if not self.root_type:
@@ -260,73 +315,99 @@ def on_trash(self):
super(Account, self).on_trash(True)
+
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.sql("""select name from tabAccount
+ 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, %s""" %
- ("%s", searchfield, "%s", "%s", "%s"),
- (filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)
+ and %s like %s order by name limit %s, %s"""
+ % ("%s", searchfield, "%s", "%s", "%s"),
+ (filters["company"], "%%%s%%" % txt, start, page_len),
+ as_list=1,
+ )
+
def get_account_currency(account):
"""Helper function to get account currency"""
if not account:
return
+
def generator():
- account_currency, company = frappe.get_cached_value("Account", account, ["account_currency", "company"])
+ account_currency, company = frappe.get_cached_value(
+ "Account", account, ["account_currency", "company"]
+ )
if not account_currency:
- account_currency = frappe.get_cached_value('Company', company, "default_currency")
+ account_currency = frappe.get_cached_value("Company", company, "default_currency")
return account_currency
return frappe.local_cache("account_currency", account, generator)
+
def on_doctype_update():
frappe.db.add_index("Account", ["lft", "rgt"])
+
def get_account_autoname(account_number, account_name, company):
# first validate if company exists
- company = frappe.get_cached_value('Company', company, ["abbr", "name"], as_dict=True)
+ company = frappe.get_cached_value("Company", company, ["abbr", "name"], as_dict=True)
if not company:
- frappe.throw(_('Company {0} does not exist').format(company))
+ frappe.throw(_("Company {0} does not exist").format(company))
parts = [account_name.strip(), company.abbr]
if cstr(account_number).strip():
parts.insert(0, cstr(account_number).strip())
- return ' - '.join(parts)
+ return " - ".join(parts)
+
def validate_account_number(name, account_number, company):
if account_number:
- account_with_same_number = frappe.db.get_value("Account",
- {"account_number": account_number, "company": company, "name": ["!=", name]})
+ account_with_same_number = frappe.db.get_value(
+ "Account", {"account_number": account_number, "company": company, "name": ["!=", name]}
+ )
if account_with_same_number:
- frappe.throw(_("Account Number {0} already used in account {1}")
- .format(account_number, account_with_same_number))
+ frappe.throw(
+ _("Account Number {0} already used in account {1}").format(
+ account_number, account_with_same_number
+ )
+ )
+
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None, from_descendant=False):
account = frappe.db.get_value("Account", name, "company", as_dict=True)
- if not account: return
+ if not account:
+ return
- old_acc_name, old_acc_number = frappe.db.get_value('Account', name, \
- ["account_name", "account_number"])
+ old_acc_name, old_acc_number = frappe.db.get_value(
+ "Account", name, ["account_name", "account_number"]
+ )
# check if account exists in parent company
ancestors = get_ancestors_of("Company", account.company)
- allow_independent_account_creation = frappe.get_value("Company", account.company, "allow_account_creation_against_child_company")
+ allow_independent_account_creation = frappe.get_value(
+ "Company", account.company, "allow_account_creation_against_child_company"
+ )
if ancestors and not allow_independent_account_creation:
for ancestor in ancestors:
- if frappe.db.get_value("Account", {'account_name': old_acc_name, 'company': ancestor}, 'name'):
+ if frappe.db.get_value("Account", {"account_name": old_acc_name, "company": ancestor}, "name"):
# same account in parent company exists
allow_child_account_creation = _("Allow Account Creation Against Child Company")
- message = _("Account {0} exists in parent company {1}.").format(frappe.bold(old_acc_name), frappe.bold(ancestor))
+ message = _("Account {0} exists in parent company {1}.").format(
+ frappe.bold(old_acc_name), frappe.bold(ancestor)
+ )
message += "
"
- message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(frappe.bold(ancestor))
+ message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(
+ frappe.bold(ancestor)
+ )
message += "
"
- message += _("To overrule this, enable '{0}' in company {1}").format(allow_child_account_creation, frappe.bold(account.company))
+ message += _("To overrule this, enable '{0}' in company {1}").format(
+ allow_child_account_creation, frappe.bold(account.company)
+ )
frappe.throw(message, title=_("Rename Not Allowed"))
@@ -339,42 +420,53 @@ def update_account_number(name, account_name, account_number=None, from_descenda
if not from_descendant:
# Update and rename in child company accounts as well
- descendants = get_descendants_of('Company', account.company)
+ descendants = get_descendants_of("Company", account.company)
if descendants:
- sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number, old_acc_number)
+ sync_update_account_number_in_child(
+ descendants, old_acc_name, account_name, account_number, old_acc_number
+ )
new_name = get_account_autoname(account_number, account_name, account.company)
if name != new_name:
frappe.rename_doc("Account", name, new_name, force=1)
return new_name
+
@frappe.whitelist()
def merge_account(old, new, is_group, root_type, company):
# Validate properties before merging
if not frappe.db.exists("Account", new):
throw(_("Account {0} does not exist").format(new))
- val = list(frappe.db.get_value("Account", new,
- ["is_group", "root_type", "company"]))
+ val = list(frappe.db.get_value("Account", new, ["is_group", "root_type", "company"]))
if val != [cint(is_group), root_type, company]:
- throw(_("""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""))
+ throw(
+ _(
+ """Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""
+ )
+ )
if is_group and frappe.db.get_value("Account", new, "parent_account") == old:
- frappe.db.set_value("Account", new, "parent_account",
- frappe.db.get_value("Account", old, "parent_account"))
+ frappe.db.set_value(
+ "Account", new, "parent_account", frappe.db.get_value("Account", old, "parent_account")
+ )
frappe.rename_doc("Account", old, new, merge=1, force=1)
return new
+
@frappe.whitelist()
def get_root_company(company):
# return the topmost company in the hierarchy
- ancestors = get_ancestors_of('Company', company, "lft asc")
+ ancestors = get_ancestors_of("Company", company, "lft asc")
return [ancestors[0]] if ancestors else []
-def sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number=None, old_acc_number=None):
+
+def sync_update_account_number_in_child(
+ descendants, old_acc_name, account_name, account_number=None, old_acc_number=None
+):
filters = {
"company": ["in", descendants],
"account_name": old_acc_name,
@@ -382,5 +474,7 @@ def sync_update_account_number_in_child(descendants, old_acc_name, account_name,
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):
- update_account_number(d["name"], account_name, account_number, from_descendant=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)
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index a8de06cc6c30..947b4853e859 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -10,7 +10,9 @@
from unidecode import unidecode
-def create_charts(company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None):
+def create_charts(
+ company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None
+):
chart = custom_chart or get_chart(chart_template, existing_company)
if chart:
accounts = []
@@ -20,30 +22,41 @@ def _import_accounts(children, parent, root_type, root_account=False):
if root_account:
root_type = child.get("root_type")
- if account_name not in ["account_name", "account_number", "account_type",
- "root_type", "is_group", "tax_rate"]:
+ if account_name not in [
+ "account_name",
+ "account_number",
+ "account_type",
+ "root_type",
+ "is_group",
+ "tax_rate",
+ ]:
account_number = cstr(child.get("account_number")).strip()
- account_name, account_name_in_db = add_suffix_if_duplicate(account_name,
- account_number, accounts)
+ account_name, account_name_in_db = add_suffix_if_duplicate(
+ account_name, account_number, accounts
+ )
is_group = identify_is_group(child)
- report_type = "Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] \
- else "Profit and Loss"
-
- account = frappe.get_doc({
- "doctype": "Account",
- "account_name": child.get('account_name') if from_coa_importer else account_name,
- "company": company,
- "parent_account": parent,
- "is_group": is_group,
- "root_type": root_type,
- "report_type": report_type,
- "account_number": account_number,
- "account_type": child.get("account_type"),
- "account_currency": child.get('account_currency') or frappe.db.get_value('Company', company, "default_currency"),
- "tax_rate": child.get("tax_rate")
- })
+ report_type = (
+ "Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
+ )
+
+ account = frappe.get_doc(
+ {
+ "doctype": "Account",
+ "account_name": child.get("account_name") if from_coa_importer else account_name,
+ "company": company,
+ "parent_account": parent,
+ "is_group": is_group,
+ "root_type": root_type,
+ "report_type": report_type,
+ "account_number": account_number,
+ "account_type": child.get("account_type"),
+ "account_currency": child.get("account_currency")
+ or frappe.db.get_value("Company", company, "default_currency"),
+ "tax_rate": child.get("tax_rate"),
+ }
+ )
if root_account or frappe.local.flags.allow_unverified_charts:
account.flags.ignore_mandatory = True
@@ -63,10 +76,10 @@ def _import_accounts(children, parent, root_type, root_account=False):
rebuild_tree("Account", "parent_account")
frappe.local.flags.ignore_update_nsm = False
+
def add_suffix_if_duplicate(account_name, account_number, accounts):
if account_number:
- account_name_in_db = unidecode(" - ".join([account_number,
- account_name.strip().lower()]))
+ account_name_in_db = unidecode(" - ".join([account_number, account_name.strip().lower()]))
else:
account_name_in_db = unidecode(account_name.strip().lower())
@@ -76,16 +89,21 @@ def add_suffix_if_duplicate(account_name, account_number, accounts):
return account_name, account_name_in_db
+
def identify_is_group(child):
if child.get("is_group"):
is_group = child.get("is_group")
- elif len(set(child.keys()) - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])):
+ elif len(
+ set(child.keys())
+ - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])
+ ):
is_group = 1
else:
is_group = 0
return is_group
+
def get_chart(chart_template, existing_company=None):
chart = {}
if existing_company:
@@ -95,11 +113,13 @@ def get_chart(chart_template, existing_company=None):
from erpnext.accounts.doctype.account.chart_of_accounts.verified import (
standard_chart_of_accounts,
)
+
return standard_chart_of_accounts.get()
elif chart_template == "Standard with Numbers":
from erpnext.accounts.doctype.account.chart_of_accounts.verified import (
standard_chart_of_accounts_with_account_number,
)
+
return standard_chart_of_accounts_with_account_number.get()
else:
folders = ("verified",)
@@ -115,6 +135,7 @@ def get_chart(chart_template, existing_company=None):
if chart and json.loads(chart).get("name") == chart_template:
return json.loads(chart).get("tree")
+
@frappe.whitelist()
def get_charts_for_country(country, with_standard=False):
charts = []
@@ -122,9 +143,10 @@ def get_charts_for_country(country, with_standard=False):
def _get_chart_name(content):
if content:
content = json.loads(content)
- if (content and content.get("disabled", "No") == "No") \
- or frappe.local.flags.allow_unverified_charts:
- charts.append(content["name"])
+ if (
+ content and content.get("disabled", "No") == "No"
+ ) or frappe.local.flags.allow_unverified_charts:
+ charts.append(content["name"])
country_code = frappe.db.get_value("Country", country, "code")
if country_code:
@@ -151,11 +173,21 @@ def _get_chart_name(content):
def get_account_tree_from_existing_company(existing_company):
- all_accounts = frappe.get_all('Account',
- filters={'company': existing_company},
- fields = ["name", "account_name", "parent_account", "account_type",
- "is_group", "root_type", "tax_rate", "account_number"],
- order_by="lft, rgt")
+ all_accounts = frappe.get_all(
+ "Account",
+ filters={"company": existing_company},
+ fields=[
+ "name",
+ "account_name",
+ "parent_account",
+ "account_type",
+ "is_group",
+ "root_type",
+ "tax_rate",
+ "account_number",
+ ],
+ order_by="lft, rgt",
+ )
account_tree = {}
@@ -164,6 +196,7 @@ def get_account_tree_from_existing_company(existing_company):
build_account_tree(account_tree, None, all_accounts)
return account_tree
+
def build_account_tree(tree, parent, all_accounts):
# find children
parent_account = parent.name if parent else ""
@@ -192,27 +225,29 @@ def build_account_tree(tree, parent, all_accounts):
# call recursively to build a subtree for current account
build_account_tree(tree[child.account_name], child, all_accounts)
+
@frappe.whitelist()
def validate_bank_account(coa, bank_account):
accounts = []
chart = get_chart(coa)
if chart:
+
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)
_get_account_names(chart)
- return (bank_account in accounts)
+ return bank_account in accounts
+
@frappe.whitelist()
def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
- ''' get chart template from its folder and parse the json to be rendered as tree '''
+ """get chart template from its folder and parse the json to be rendered as tree"""
chart = chart_data or get_chart(chart_template)
# if no template selected, return as it is
@@ -220,22 +255,33 @@ def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=Fals
return
accounts = []
+
def _import_accounts(children, parent):
- ''' recursively called to form a parent-child based list of dict from chart template '''
+ """recursively called to form a parent-child based list of dict from chart template"""
for account_name, child in children.items():
account = {}
- if account_name in ["account_name", "account_number", "account_type",\
- "root_type", "is_group", "tax_rate"]: continue
+ if account_name in [
+ "account_name",
+ "account_number",
+ "account_type",
+ "root_type",
+ "is_group",
+ "tax_rate",
+ ]:
+ continue
if from_coa_importer:
- account_name = child['account_name']
-
- account['parent_account'] = parent
- account['expandable'] = True if identify_is_group(child) else False
- account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \
- if child.get('account_number') else account_name
+ account_name = child["account_name"]
+
+ account["parent_account"] = parent
+ account["expandable"] = True if identify_is_group(child) else False
+ account["value"] = (
+ (cstr(child.get("account_number")).strip() + " - " + account_name)
+ if child.get("account_number")
+ else account_name
+ )
accounts.append(account)
- _import_accounts(child, account['value'])
+ _import_accounts(child, account["value"])
_import_accounts(chart, None)
return accounts
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py b/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py
index 79001d770538..3f25ada8b353 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py
@@ -20,6 +20,7 @@
all_account_types = []
all_roots = {}
+
def go():
global accounts, charts
default_account_types = get_default_account_types()
@@ -34,14 +35,16 @@ def go():
accounts, charts = {}, {}
country_path = os.path.join(path, country_dir)
manifest = ast.literal_eval(open(os.path.join(country_path, "__openerp__.py")).read())
- data_files = manifest.get("data", []) + manifest.get("init_xml", []) + \
- manifest.get("update_xml", [])
+ data_files = (
+ manifest.get("data", []) + manifest.get("init_xml", []) + manifest.get("update_xml", [])
+ )
files_path = [os.path.join(country_path, d) for d in data_files]
xml_roots = get_xml_roots(files_path)
csv_content = get_csv_contents(files_path)
prefix = country_dir if csv_content else None
- account_types = get_account_types(xml_roots.get("account.account.type", []),
- csv_content.get("account.account.type", []), prefix)
+ account_types = get_account_types(
+ xml_roots.get("account.account.type", []), csv_content.get("account.account.type", []), prefix
+ )
account_types.update(default_account_types)
if xml_roots:
@@ -54,12 +57,15 @@ def go():
create_all_roots_file()
+
def get_default_account_types():
default_types_root = []
- default_types_root.append(ET.parse(os.path.join(path, "account", "data",
- "data_account_type.xml")).getroot())
+ default_types_root.append(
+ ET.parse(os.path.join(path, "account", "data", "data_account_type.xml")).getroot()
+ )
return get_account_types(default_types_root, None, prefix="account")
+
def get_xml_roots(files_path):
xml_roots = frappe._dict()
for filepath in files_path:
@@ -68,64 +74,69 @@ def get_xml_roots(files_path):
tree = ET.parse(filepath)
root = tree.getroot()
for node in root[0].findall("record"):
- if node.get("model") in ["account.account.template",
- "account.chart.template", "account.account.type"]:
+ if node.get("model") in [
+ "account.account.template",
+ "account.chart.template",
+ "account.account.type",
+ ]:
xml_roots.setdefault(node.get("model"), []).append(root)
break
return xml_roots
+
def get_csv_contents(files_path):
csv_content = {}
for filepath in files_path:
fname = os.path.basename(filepath)
- for file_type in ["account.account.template", "account.account.type",
- "account.chart.template"]:
+ for file_type in ["account.account.template", "account.account.type", "account.chart.template"]:
if fname.startswith(file_type) and fname.endswith(".csv"):
with open(filepath, "r") as csvfile:
try:
- csv_content.setdefault(file_type, [])\
- .append(read_csv_content(csvfile.read()))
+ csv_content.setdefault(file_type, []).append(read_csv_content(csvfile.read()))
except Exception as e:
continue
return csv_content
+
def get_account_types(root_list, csv_content, prefix=None):
types = {}
account_type_map = {
- 'cash': 'Cash',
- 'bank': 'Bank',
- 'tr_cash': 'Cash',
- 'tr_bank': 'Bank',
- 'receivable': 'Receivable',
- 'tr_receivable': 'Receivable',
- 'account rec': 'Receivable',
- 'payable': 'Payable',
- 'tr_payable': 'Payable',
- 'equity': 'Equity',
- 'stocks': 'Stock',
- 'stock': 'Stock',
- 'tax': 'Tax',
- 'tr_tax': 'Tax',
- 'tax-out': 'Tax',
- 'tax-in': 'Tax',
- 'charges_personnel': 'Chargeable',
- 'fixed asset': 'Fixed Asset',
- 'cogs': 'Cost of Goods Sold',
-
+ "cash": "Cash",
+ "bank": "Bank",
+ "tr_cash": "Cash",
+ "tr_bank": "Bank",
+ "receivable": "Receivable",
+ "tr_receivable": "Receivable",
+ "account rec": "Receivable",
+ "payable": "Payable",
+ "tr_payable": "Payable",
+ "equity": "Equity",
+ "stocks": "Stock",
+ "stock": "Stock",
+ "tax": "Tax",
+ "tr_tax": "Tax",
+ "tax-out": "Tax",
+ "tax-in": "Tax",
+ "charges_personnel": "Chargeable",
+ "fixed asset": "Fixed Asset",
+ "cogs": "Cost of Goods Sold",
}
for root in root_list:
for node in root[0].findall("record"):
- if node.get("model")=="account.account.type":
+ if node.get("model") == "account.account.type":
data = {}
for field in node.findall("field"):
- if field.get("name")=="code" and field.text.lower() != "none" \
- and account_type_map.get(field.text):
- data["account_type"] = account_type_map[field.text]
+ if (
+ field.get("name") == "code"
+ and field.text.lower() != "none"
+ and account_type_map.get(field.text)
+ ):
+ data["account_type"] = account_type_map[field.text]
node_id = prefix + "." + node.get("id") if prefix else node.get("id")
types[node_id] = data
- if csv_content and csv_content[0][0]=="id":
+ if csv_content and csv_content[0][0] == "id":
for row in csv_content[1:]:
row_dict = dict(zip(csv_content[0], row))
data = {}
@@ -136,21 +147,22 @@ def get_account_types(root_list, csv_content, prefix=None):
types[node_id] = data
return types
+
def make_maps_for_xml(xml_roots, account_types, country_dir):
"""make maps for `charts` and `accounts`"""
for model, root_list in xml_roots.items():
for root in root_list:
for node in root[0].findall("record"):
- if node.get("model")=="account.account.template":
+ if node.get("model") == "account.account.template":
data = {}
for field in node.findall("field"):
- if field.get("name")=="name":
+ if field.get("name") == "name":
data["name"] = field.text
- if field.get("name")=="parent_id":
+ if field.get("name") == "parent_id":
parent_id = field.get("ref") or field.get("eval")
data["parent_id"] = parent_id
- if field.get("name")=="user_type":
+ if field.get("name") == "user_type":
value = field.get("ref")
if account_types.get(value, {}).get("account_type"):
data["account_type"] = account_types[value]["account_type"]
@@ -160,16 +172,17 @@ def make_maps_for_xml(xml_roots, account_types, country_dir):
data["children"] = []
accounts[node.get("id")] = data
- if node.get("model")=="account.chart.template":
+ if node.get("model") == "account.chart.template":
data = {}
for field in node.findall("field"):
- if field.get("name")=="name":
+ if field.get("name") == "name":
data["name"] = field.text
- if field.get("name")=="account_root_id":
+ if field.get("name") == "account_root_id":
data["account_root_id"] = field.get("ref")
data["id"] = country_dir
charts.setdefault(node.get("id"), {}).update(data)
+
def make_maps_for_csv(csv_content, account_types, country_dir):
for content in csv_content.get("account.account.template", []):
for row in content[1:]:
@@ -177,7 +190,7 @@ def make_maps_for_csv(csv_content, account_types, country_dir):
account = {
"name": data.get("name"),
"parent_id": data.get("parent_id:id") or data.get("parent_id/id"),
- "children": []
+ "children": [],
}
user_type = data.get("user_type/id") or data.get("user_type:id")
if account_types.get(user_type, {}).get("account_type"):
@@ -194,12 +207,14 @@ def make_maps_for_csv(csv_content, account_types, country_dir):
for row in content[1:]:
if row:
data = dict(zip(content[0], row))
- charts.setdefault(data.get("id"), {}).update({
- "account_root_id": data.get("account_root_id:id") or \
- data.get("account_root_id/id"),
- "name": data.get("name"),
- "id": country_dir
- })
+ charts.setdefault(data.get("id"), {}).update(
+ {
+ "account_root_id": data.get("account_root_id:id") or data.get("account_root_id/id"),
+ "name": data.get("name"),
+ "id": country_dir,
+ }
+ )
+
def make_account_trees():
"""build tree hierarchy"""
@@ -218,6 +233,7 @@ def make_account_trees():
if "children" in accounts[id] and not accounts[id].get("children"):
del accounts[id]["children"]
+
def make_charts():
"""write chart files in app/setup/doctype/company/charts"""
for chart_id in charts:
@@ -236,34 +252,38 @@ def make_charts():
chart["country_code"] = src["id"][5:]
chart["tree"] = accounts[src["account_root_id"]]
-
for key, val in chart["tree"].items():
if key in ["name", "parent_id"]:
chart["tree"].pop(key)
if type(val) == dict:
val["root_type"] = ""
if chart:
- fpath = os.path.join("erpnext", "erpnext", "accounts", "doctype", "account",
- "chart_of_accounts", filename + ".json")
+ fpath = os.path.join(
+ "erpnext", "erpnext", "accounts", "doctype", "account", "chart_of_accounts", filename + ".json"
+ )
with open(fpath, "r") as chartfile:
old_content = chartfile.read()
- if not old_content or (json.loads(old_content).get("is_active", "No") == "No" \
- and json.loads(old_content).get("disabled", "No") == "No"):
+ if not old_content or (
+ json.loads(old_content).get("is_active", "No") == "No"
+ and json.loads(old_content).get("disabled", "No") == "No"
+ ):
with open(fpath, "w") as chartfile:
chartfile.write(json.dumps(chart, indent=4, sort_keys=True))
all_roots.setdefault(filename, chart["tree"].keys())
+
def create_all_roots_file():
- with open('all_roots.txt', 'w') as f:
+ with open("all_roots.txt", "w") as f:
for filename, roots in sorted(all_roots.items()):
f.write(filename)
- f.write('\n----------------------\n')
+ f.write("\n----------------------\n")
for r in sorted(roots):
- f.write(r.encode('utf-8'))
- f.write('\n')
- f.write('\n\n\n')
+ f.write(r.encode("utf-8"))
+ f.write("\n")
+ f.write("\n\n\n")
+
-if __name__=="__main__":
+if __name__ == "__main__":
go()
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
index 9248ffa6e57b..e30ad24a374a 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
@@ -7,182 +7,103 @@
def get():
return {
- _("Application of Funds (Assets)"): {
- _("Current Assets"): {
- _("Accounts Receivable"): {
- _("Debtors"): {
- "account_type": "Receivable"
- }
- },
- _("Bank Accounts"): {
- "account_type": "Bank",
- "is_group": 1
- },
- _("Cash In Hand"): {
- _("Cash"): {
- "account_type": "Cash"
- },
- "account_type": "Cash"
- },
- _("Loans and Advances (Assets)"): {
- _("Employee Advances"): {
- },
- },
- _("Securities and Deposits"): {
- _("Earnest Money"): {}
- },
- _("Stock Assets"): {
- _("Stock In Hand"): {
- "account_type": "Stock"
- },
- "account_type": "Stock",
- },
- _("Tax Assets"): {
- "is_group": 1
- }
- },
- _("Fixed Assets"): {
- _("Capital Equipments"): {
- "account_type": "Fixed Asset"
- },
- _("Electronic Equipments"): {
- "account_type": "Fixed Asset"
- },
- _("Furnitures and Fixtures"): {
- "account_type": "Fixed Asset"
- },
- _("Office Equipments"): {
- "account_type": "Fixed Asset"
- },
- _("Plants and Machineries"): {
- "account_type": "Fixed Asset"
- },
- _("Buildings"): {
- "account_type": "Fixed Asset"
+ _("Application of Funds (Assets)"): {
+ _("Current Assets"): {
+ _("Accounts Receivable"): {_("Debtors"): {"account_type": "Receivable"}},
+ _("Bank Accounts"): {"account_type": "Bank", "is_group": 1},
+ _("Cash In Hand"): {_("Cash"): {"account_type": "Cash"}, "account_type": "Cash"},
+ _("Loans and Advances (Assets)"): {
+ _("Employee Advances"): {},
},
- _("Softwares"): {
- "account_type": "Fixed Asset"
+ _("Securities and Deposits"): {_("Earnest Money"): {}},
+ _("Stock Assets"): {
+ _("Stock In Hand"): {"account_type": "Stock"},
+ "account_type": "Stock",
},
- _("Accumulated Depreciation"): {
- "account_type": "Accumulated Depreciation"
- },
- _("CWIP Account"): {
- "account_type": "Capital Work in Progress",
- }
- },
- _("Investments"): {
- "is_group": 1
- },
- _("Temporary Accounts"): {
- _("Temporary Opening"): {
- "account_type": "Temporary"
- }
- },
- "root_type": "Asset"
- },
- _("Expenses"): {
- _("Direct Expenses"): {
- _("Stock Expenses"): {
- _("Cost of Goods Sold"): {
- "account_type": "Cost of Goods Sold"
- },
- _("Expenses Included In Asset Valuation"): {
- "account_type": "Expenses Included In Asset Valuation"
- },
- _("Expenses Included In Valuation"): {
- "account_type": "Expenses Included In Valuation"
- },
- _("Stock Adjustment"): {
- "account_type": "Stock Adjustment"
- }
- },
- },
- _("Indirect Expenses"): {
- _("Administrative Expenses"): {},
- _("Commission on Sales"): {},
- _("Depreciation"): {
- "account_type": "Depreciation"
- },
- _("Entertainment Expenses"): {},
- _("Freight and Forwarding Charges"): {
- "account_type": "Chargeable"
- },
- _("Legal Expenses"): {},
- _("Marketing Expenses"): {
- "account_type": "Chargeable"
- },
- _("Miscellaneous Expenses"): {
- "account_type": "Chargeable"
- },
- _("Office Maintenance Expenses"): {},
- _("Office Rent"): {},
- _("Postal Expenses"): {},
- _("Print and Stationery"): {},
- _("Round Off"): {
- "account_type": "Round Off"
- },
- _("Salary"): {},
- _("Sales Expenses"): {},
- _("Telephone Expenses"): {},
- _("Travel Expenses"): {},
- _("Utility Expenses"): {},
+ _("Tax Assets"): {"is_group": 1},
+ },
+ _("Fixed Assets"): {
+ _("Capital Equipments"): {"account_type": "Fixed Asset"},
+ _("Electronic Equipments"): {"account_type": "Fixed Asset"},
+ _("Furnitures and Fixtures"): {"account_type": "Fixed Asset"},
+ _("Office Equipments"): {"account_type": "Fixed Asset"},
+ _("Plants and Machineries"): {"account_type": "Fixed Asset"},
+ _("Buildings"): {"account_type": "Fixed Asset"},
+ _("Softwares"): {"account_type": "Fixed Asset"},
+ _("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
+ _("CWIP Account"): {
+ "account_type": "Capital Work in Progress",
+ },
+ },
+ _("Investments"): {"is_group": 1},
+ _("Temporary Accounts"): {_("Temporary Opening"): {"account_type": "Temporary"}},
+ "root_type": "Asset",
+ },
+ _("Expenses"): {
+ _("Direct Expenses"): {
+ _("Stock Expenses"): {
+ _("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold"},
+ _("Expenses Included In Asset Valuation"): {
+ "account_type": "Expenses Included In Asset Valuation"
+ },
+ _("Expenses Included In Valuation"): {"account_type": "Expenses Included In Valuation"},
+ _("Stock Adjustment"): {"account_type": "Stock Adjustment"},
+ },
+ },
+ _("Indirect Expenses"): {
+ _("Administrative Expenses"): {},
+ _("Commission on Sales"): {},
+ _("Depreciation"): {"account_type": "Depreciation"},
+ _("Entertainment Expenses"): {},
+ _("Freight and Forwarding Charges"): {"account_type": "Chargeable"},
+ _("Legal Expenses"): {},
+ _("Marketing Expenses"): {"account_type": "Chargeable"},
+ _("Miscellaneous Expenses"): {"account_type": "Chargeable"},
+ _("Office Maintenance Expenses"): {},
+ _("Office Rent"): {},
+ _("Postal Expenses"): {},
+ _("Print and Stationery"): {},
+ _("Round Off"): {"account_type": "Round Off"},
+ _("Salary"): {},
+ _("Sales Expenses"): {},
+ _("Telephone Expenses"): {},
+ _("Travel Expenses"): {},
+ _("Utility Expenses"): {},
_("Write Off"): {},
_("Exchange Gain/Loss"): {},
- _("Gain/Loss on Asset Disposal"): {}
- },
- "root_type": "Expense"
- },
- _("Income"): {
- _("Direct Income"): {
- _("Sales"): {},
- _("Service"): {}
- },
- _("Indirect Income"): {
- "is_group": 1
- },
- "root_type": "Income"
- },
- _("Source of Funds (Liabilities)"): {
- _("Current Liabilities"): {
- _("Accounts Payable"): {
- _("Creditors"): {
- "account_type": "Payable"
- },
- _("Payroll Payable"): {},
- },
- _("Stock Liabilities"): {
- _("Stock Received But Not Billed"): {
- "account_type": "Stock Received But Not Billed"
- },
- _("Asset Received But Not Billed"): {
- "account_type": "Asset Received But Not Billed"
- }
- },
- _("Duties and Taxes"): {
- "account_type": "Tax",
- "is_group": 1
+ _("Gain/Loss on Asset Disposal"): {},
+ },
+ "root_type": "Expense",
+ },
+ _("Income"): {
+ _("Direct Income"): {_("Sales"): {}, _("Service"): {}},
+ _("Indirect Income"): {"is_group": 1},
+ "root_type": "Income",
+ },
+ _("Source of Funds (Liabilities)"): {
+ _("Current Liabilities"): {
+ _("Accounts Payable"): {
+ _("Creditors"): {"account_type": "Payable"},
+ _("Payroll Payable"): {},
+ },
+ _("Stock Liabilities"): {
+ _("Stock Received But Not Billed"): {"account_type": "Stock Received But Not Billed"},
+ _("Asset Received But Not Billed"): {"account_type": "Asset Received But Not Billed"},
},
+ _("Duties and Taxes"): {"account_type": "Tax", "is_group": 1},
_("Loans (Liabilities)"): {
_("Secured Loans"): {},
_("Unsecured Loans"): {},
_("Bank Overdraft Account"): {},
},
- },
- "root_type": "Liability"
- },
+ },
+ "root_type": "Liability",
+ },
_("Equity"): {
- _("Capital Stock"): {
- "account_type": "Equity"
- },
- _("Dividends Paid"): {
- "account_type": "Equity"
- },
- _("Opening Balance Equity"): {
- "account_type": "Equity"
- },
- _("Retained Earnings"): {
- "account_type": "Equity"
- },
- "root_type": "Equity"
- }
+ _("Capital Stock"): {"account_type": "Equity"},
+ _("Dividends Paid"): {"account_type": "Equity"},
+ _("Opening Balance Equity"): {"account_type": "Equity"},
+ _("Retained Earnings"): {"account_type": "Equity"},
+ "root_type": "Equity",
+ },
}
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
index 31ae17189a71..0e46f1e08a5e 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
@@ -6,288 +6,153 @@
def get():
- return {
- _("Application of Funds (Assets)"): {
- _("Current Assets"): {
- _("Accounts Receivable"): {
- _("Debtors"): {
- "account_type": "Receivable",
- "account_number": "1310"
- },
- "account_number": "1300"
- },
- _("Bank Accounts"): {
- "account_type": "Bank",
- "is_group": 1,
- "account_number": "1200"
- },
- _("Cash In Hand"): {
- _("Cash"): {
- "account_type": "Cash",
- "account_number": "1110"
- },
- "account_type": "Cash",
- "account_number": "1100"
- },
- _("Loans and Advances (Assets)"): {
- _("Employee Advances"): {
- "account_number": "1610"
- },
- "account_number": "1600"
- },
- _("Securities and Deposits"): {
- _("Earnest Money"): {
- "account_number": "1651"
- },
- "account_number": "1650"
- },
- _("Stock Assets"): {
- _("Stock In Hand"): {
- "account_type": "Stock",
- "account_number": "1410"
- },
- "account_type": "Stock",
- "account_number": "1400"
- },
- _("Tax Assets"): {
- "is_group": 1,
- "account_number": "1500"
- },
- "account_number": "1100-1600"
- },
- _("Fixed Assets"): {
- _("Capital Equipments"): {
- "account_type": "Fixed Asset",
- "account_number": "1710"
- },
- _("Electronic Equipments"): {
- "account_type": "Fixed Asset",
- "account_number": "1720"
- },
- _("Furnitures and Fixtures"): {
- "account_type": "Fixed Asset",
- "account_number": "1730"
- },
- _("Office Equipments"): {
- "account_type": "Fixed Asset",
- "account_number": "1740"
- },
- _("Plants and Machineries"): {
- "account_type": "Fixed Asset",
- "account_number": "1750"
- },
- _("Buildings"): {
- "account_type": "Fixed Asset",
- "account_number": "1760"
- },
- _("Softwares"): {
- "account_type": "Fixed Asset",
- "account_number": "1770"
- },
- _("Accumulated Depreciation"): {
- "account_type": "Accumulated Depreciation",
- "account_number": "1780"
- },
- _("CWIP Account"): {
- "account_type": "Capital Work in Progress",
- "account_number": "1790"
- },
- "account_number": "1700"
- },
- _("Investments"): {
- "is_group": 1,
- "account_number": "1800"
- },
- _("Temporary Accounts"): {
- _("Temporary Opening"): {
- "account_type": "Temporary",
- "account_number": "1910"
- },
- "account_number": "1900"
- },
- "root_type": "Asset",
- "account_number": "1000"
- },
- _("Expenses"): {
- _("Direct Expenses"): {
- _("Stock Expenses"): {
- _("Cost of Goods Sold"): {
- "account_type": "Cost of Goods Sold",
- "account_number": "5111"
- },
- _("Expenses Included In Asset Valuation"): {
- "account_type": "Expenses Included In Asset Valuation",
- "account_number": "5112"
- },
- _("Expenses Included In Valuation"): {
- "account_type": "Expenses Included In Valuation",
- "account_number": "5118"
- },
- _("Stock Adjustment"): {
- "account_type": "Stock Adjustment",
- "account_number": "5119"
- },
- "account_number": "5110"
- },
- "account_number": "5100"
- },
- _("Indirect Expenses"): {
- _("Administrative Expenses"): {
- "account_number": "5201"
- },
- _("Commission on Sales"): {
- "account_number": "5202"
- },
- _("Depreciation"): {
- "account_type": "Depreciation",
- "account_number": "5203"
- },
- _("Entertainment Expenses"): {
- "account_number": "5204"
- },
- _("Freight and Forwarding Charges"): {
- "account_type": "Chargeable",
- "account_number": "5205"
- },
- _("Legal Expenses"): {
- "account_number": "5206"
- },
- _("Marketing Expenses"): {
- "account_type": "Chargeable",
- "account_number": "5207"
- },
- _("Office Maintenance Expenses"): {
- "account_number": "5208"
- },
- _("Office Rent"): {
- "account_number": "5209"
- },
- _("Postal Expenses"): {
- "account_number": "5210"
- },
- _("Print and Stationery"): {
- "account_number": "5211"
- },
- _("Round Off"): {
- "account_type": "Round Off",
- "account_number": "5212"
- },
- _("Salary"): {
- "account_number": "5213"
- },
- _("Sales Expenses"): {
- "account_number": "5214"
- },
- _("Telephone Expenses"): {
- "account_number": "5215"
- },
- _("Travel Expenses"): {
- "account_number": "5216"
- },
- _("Utility Expenses"): {
- "account_number": "5217"
- },
- _("Write Off"): {
- "account_number": "5218"
- },
- _("Exchange Gain/Loss"): {
- "account_number": "5219"
- },
- _("Gain/Loss on Asset Disposal"): {
- "account_number": "5220"
- },
- _("Miscellaneous Expenses"): {
- "account_type": "Chargeable",
- "account_number": "5221"
- },
- "account_number": "5200"
- },
- "root_type": "Expense",
- "account_number": "5000"
- },
- _("Income"): {
- _("Direct Income"): {
- _("Sales"): {
- "account_number": "4110"
- },
- _("Service"): {
- "account_number": "4120"
- },
- "account_number": "4100"
- },
- _("Indirect Income"): {
- "is_group": 1,
- "account_number": "4200"
- },
- "root_type": "Income",
- "account_number": "4000"
- },
- _("Source of Funds (Liabilities)"): {
- _("Current Liabilities"): {
- _("Accounts Payable"): {
- _("Creditors"): {
- "account_type": "Payable",
- "account_number": "2110"
- },
- _("Payroll Payable"): {
- "account_number": "2120"
- },
- "account_number": "2100"
- },
- _("Stock Liabilities"): {
- _("Stock Received But Not Billed"): {
- "account_type": "Stock Received But Not Billed",
- "account_number": "2210"
- },
- _("Asset Received But Not Billed"): {
- "account_type": "Asset Received But Not Billed",
- "account_number": "2211"
- },
- "account_number": "2200"
- },
- _("Duties and Taxes"): {
- _("TDS Payable"): {
- "account_number": "2310"
- },
- "account_type": "Tax",
- "is_group": 1,
- "account_number": "2300"
- },
- _("Loans (Liabilities)"): {
- _("Secured Loans"): {
- "account_number": "2410"
- },
- _("Unsecured Loans"): {
- "account_number": "2420"
- },
- _("Bank Overdraft Account"): {
- "account_number": "2430"
- },
- "account_number": "2400"
- },
- "account_number": "2100-2400"
- },
- "root_type": "Liability",
- "account_number": "2000"
- },
- _("Equity"): {
- _("Capital Stock"): {
- "account_type": "Equity",
- "account_number": "3100"
- },
- _("Dividends Paid"): {
- "account_type": "Equity",
- "account_number": "3200"
- },
- _("Opening Balance Equity"): {
- "account_type": "Equity",
- "account_number": "3300"
- },
- _("Retained Earnings"): {
- "account_type": "Equity",
- "account_number": "3400"
- },
- "root_type": "Equity",
- "account_number": "3000"
- }
- }
+ return {
+ _("Application of Funds (Assets)"): {
+ _("Current Assets"): {
+ _("Accounts Receivable"): {
+ _("Debtors"): {"account_type": "Receivable", "account_number": "1310"},
+ "account_number": "1300",
+ },
+ _("Bank Accounts"): {"account_type": "Bank", "is_group": 1, "account_number": "1200"},
+ _("Cash In Hand"): {
+ _("Cash"): {"account_type": "Cash", "account_number": "1110"},
+ "account_type": "Cash",
+ "account_number": "1100",
+ },
+ _("Loans and Advances (Assets)"): {
+ _("Employee Advances"): {"account_number": "1610"},
+ "account_number": "1600",
+ },
+ _("Securities and Deposits"): {
+ _("Earnest Money"): {"account_number": "1651"},
+ "account_number": "1650",
+ },
+ _("Stock Assets"): {
+ _("Stock In Hand"): {"account_type": "Stock", "account_number": "1410"},
+ "account_type": "Stock",
+ "account_number": "1400",
+ },
+ _("Tax Assets"): {"is_group": 1, "account_number": "1500"},
+ "account_number": "1100-1600",
+ },
+ _("Fixed Assets"): {
+ _("Capital Equipments"): {"account_type": "Fixed Asset", "account_number": "1710"},
+ _("Electronic Equipments"): {"account_type": "Fixed Asset", "account_number": "1720"},
+ _("Furnitures and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
+ _("Office Equipments"): {"account_type": "Fixed Asset", "account_number": "1740"},
+ _("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
+ _("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
+ _("Softwares"): {"account_type": "Fixed Asset", "account_number": "1770"},
+ _("Accumulated Depreciation"): {
+ "account_type": "Accumulated Depreciation",
+ "account_number": "1780",
+ },
+ _("CWIP Account"): {"account_type": "Capital Work in Progress", "account_number": "1790"},
+ "account_number": "1700",
+ },
+ _("Investments"): {"is_group": 1, "account_number": "1800"},
+ _("Temporary Accounts"): {
+ _("Temporary Opening"): {"account_type": "Temporary", "account_number": "1910"},
+ "account_number": "1900",
+ },
+ "root_type": "Asset",
+ "account_number": "1000",
+ },
+ _("Expenses"): {
+ _("Direct Expenses"): {
+ _("Stock Expenses"): {
+ _("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold", "account_number": "5111"},
+ _("Expenses Included In Asset Valuation"): {
+ "account_type": "Expenses Included In Asset Valuation",
+ "account_number": "5112",
+ },
+ _("Expenses Included In Valuation"): {
+ "account_type": "Expenses Included In Valuation",
+ "account_number": "5118",
+ },
+ _("Stock Adjustment"): {"account_type": "Stock Adjustment", "account_number": "5119"},
+ "account_number": "5110",
+ },
+ "account_number": "5100",
+ },
+ _("Indirect Expenses"): {
+ _("Administrative Expenses"): {"account_number": "5201"},
+ _("Commission on Sales"): {"account_number": "5202"},
+ _("Depreciation"): {"account_type": "Depreciation", "account_number": "5203"},
+ _("Entertainment Expenses"): {"account_number": "5204"},
+ _("Freight and Forwarding Charges"): {"account_type": "Chargeable", "account_number": "5205"},
+ _("Legal Expenses"): {"account_number": "5206"},
+ _("Marketing Expenses"): {"account_type": "Chargeable", "account_number": "5207"},
+ _("Office Maintenance Expenses"): {"account_number": "5208"},
+ _("Office Rent"): {"account_number": "5209"},
+ _("Postal Expenses"): {"account_number": "5210"},
+ _("Print and Stationery"): {"account_number": "5211"},
+ _("Round Off"): {"account_type": "Round Off", "account_number": "5212"},
+ _("Salary"): {"account_number": "5213"},
+ _("Sales Expenses"): {"account_number": "5214"},
+ _("Telephone Expenses"): {"account_number": "5215"},
+ _("Travel Expenses"): {"account_number": "5216"},
+ _("Utility Expenses"): {"account_number": "5217"},
+ _("Write Off"): {"account_number": "5218"},
+ _("Exchange Gain/Loss"): {"account_number": "5219"},
+ _("Gain/Loss on Asset Disposal"): {"account_number": "5220"},
+ _("Miscellaneous Expenses"): {"account_type": "Chargeable", "account_number": "5221"},
+ "account_number": "5200",
+ },
+ "root_type": "Expense",
+ "account_number": "5000",
+ },
+ _("Income"): {
+ _("Direct Income"): {
+ _("Sales"): {"account_number": "4110"},
+ _("Service"): {"account_number": "4120"},
+ "account_number": "4100",
+ },
+ _("Indirect Income"): {"is_group": 1, "account_number": "4200"},
+ "root_type": "Income",
+ "account_number": "4000",
+ },
+ _("Source of Funds (Liabilities)"): {
+ _("Current Liabilities"): {
+ _("Accounts Payable"): {
+ _("Creditors"): {"account_type": "Payable", "account_number": "2110"},
+ _("Payroll Payable"): {"account_number": "2120"},
+ "account_number": "2100",
+ },
+ _("Stock Liabilities"): {
+ _("Stock Received But Not Billed"): {
+ "account_type": "Stock Received But Not Billed",
+ "account_number": "2210",
+ },
+ _("Asset Received But Not Billed"): {
+ "account_type": "Asset Received But Not Billed",
+ "account_number": "2211",
+ },
+ "account_number": "2200",
+ },
+ _("Duties and Taxes"): {
+ _("TDS Payable"): {"account_number": "2310"},
+ "account_type": "Tax",
+ "is_group": 1,
+ "account_number": "2300",
+ },
+ _("Loans (Liabilities)"): {
+ _("Secured Loans"): {"account_number": "2410"},
+ _("Unsecured Loans"): {"account_number": "2420"},
+ _("Bank Overdraft Account"): {"account_number": "2430"},
+ "account_number": "2400",
+ },
+ "account_number": "2100-2400",
+ },
+ "root_type": "Liability",
+ "account_number": "2000",
+ },
+ _("Equity"): {
+ _("Capital Stock"): {"account_type": "Equity", "account_number": "3100"},
+ _("Dividends Paid"): {"account_type": "Equity", "account_number": "3200"},
+ _("Opening Balance Equity"): {"account_type": "Equity", "account_number": "3300"},
+ _("Retained Earnings"): {"account_type": "Equity", "account_number": "3400"},
+ "root_type": "Equity",
+ "account_number": "3000",
+ },
+ }
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 0715823b3008..f9c9173af082 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -20,8 +20,9 @@ def test_rename_account(self):
acc.company = "_Test Company"
acc.insert()
- account_number, account_name = frappe.db.get_value("Account", "1210 - Debtors - _TC",
- ["account_number", "account_name"])
+ account_number, account_name = frappe.db.get_value(
+ "Account", "1210 - Debtors - _TC", ["account_number", "account_name"]
+ )
self.assertEqual(account_number, "1210")
self.assertEqual(account_name, "Debtors")
@@ -30,8 +31,12 @@ def test_rename_account(self):
update_account_number("1210 - Debtors - _TC", new_account_name, new_account_number)
- new_acc = frappe.db.get_value("Account", "1211-11-4 - 6 - - Debtors 1 - Test - - _TC",
- ["account_name", "account_number"], as_dict=1)
+ new_acc = frappe.db.get_value(
+ "Account",
+ "1211-11-4 - 6 - - Debtors 1 - Test - - _TC",
+ ["account_name", "account_number"],
+ as_dict=1,
+ )
self.assertEqual(new_acc.account_name, "Debtors 1 - Test -")
self.assertEqual(new_acc.account_number, "1211-11-4 - 6 -")
@@ -79,7 +84,9 @@ def test_merge_account(self):
self.assertEqual(parent, "Securities and Deposits - _TC")
- merge_account("Securities and Deposits - _TC", "Cash In Hand - _TC", doc.is_group, doc.root_type, doc.company)
+ merge_account(
+ "Securities and Deposits - _TC", "Cash In Hand - _TC", doc.is_group, doc.root_type, doc.company
+ )
parent = frappe.db.get_value("Account", "Earnest Money - _TC", "parent_account")
# Parent account of the child account changes after merging
@@ -91,14 +98,28 @@ def test_merge_account(self):
doc = frappe.get_doc("Account", "Current Assets - _TC")
# Raise error as is_group property doesn't match
- self.assertRaises(frappe.ValidationError, merge_account, "Current Assets - _TC",\
- "Accumulated Depreciation - _TC", doc.is_group, doc.root_type, doc.company)
+ self.assertRaises(
+ frappe.ValidationError,
+ merge_account,
+ "Current Assets - _TC",
+ "Accumulated Depreciation - _TC",
+ doc.is_group,
+ doc.root_type,
+ doc.company,
+ )
doc = frappe.get_doc("Account", "Capital Stock - _TC")
# Raise error as root_type property doesn't match
- self.assertRaises(frappe.ValidationError, merge_account, "Capital Stock - _TC",\
- "Softwares - _TC", doc.is_group, doc.root_type, doc.company)
+ self.assertRaises(
+ frappe.ValidationError,
+ merge_account,
+ "Capital Stock - _TC",
+ "Softwares - _TC",
+ doc.is_group,
+ doc.root_type,
+ doc.company,
+ )
def test_account_sync(self):
frappe.local.flags.pop("ignore_root_company_validation", None)
@@ -109,8 +130,12 @@ def test_account_sync(self):
acc.company = "_Test Company 3"
acc.insert()
- acc_tc_4 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 4"})
- acc_tc_5 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 5"})
+ acc_tc_4 = frappe.db.get_value(
+ "Account", {"account_name": "Test Sync Account", "company": "_Test Company 4"}
+ )
+ acc_tc_5 = frappe.db.get_value(
+ "Account", {"account_name": "Test Sync Account", "company": "_Test Company 5"}
+ )
self.assertEqual(acc_tc_4, "Test Sync Account - _TC4")
self.assertEqual(acc_tc_5, "Test Sync Account - _TC5")
@@ -138,8 +163,26 @@ def test_account_rename_sync(self):
update_account_number(acc.name, "Test Rename Sync Account", "1234")
# Check if renamed in children
- self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 4", "account_number": "1234"}))
- self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 5", "account_number": "1234"}))
+ self.assertTrue(
+ frappe.db.exists(
+ "Account",
+ {
+ "account_name": "Test Rename Sync Account",
+ "company": "_Test Company 4",
+ "account_number": "1234",
+ },
+ )
+ )
+ self.assertTrue(
+ frappe.db.exists(
+ "Account",
+ {
+ "account_name": "Test Rename Sync Account",
+ "company": "_Test Company 5",
+ "account_number": "1234",
+ },
+ )
+ )
frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC3")
frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC4")
@@ -155,25 +198,71 @@ def test_child_company_account_rename_sync(self):
acc.company = "_Test Company 3"
acc.insert()
- self.assertTrue(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"}))
+ self.assertTrue(
+ 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"}
+ )
+ )
# 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")
+ 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"
+ )
# 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(frappe.db.exists("Account", {'name': "Test Modified Account - _TC5", "company": "_Test Company 5"}))
-
- frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
-
- to_delete = ["Test Group Account - _TC3", "Test Group Account - _TC4", "Test Modified Account - _TC5"]
+ self.assertTrue(
+ frappe.db.exists(
+ "Account", {"name": "Test Modified Account - _TC5", "company": "_Test Company 5"}
+ )
+ )
+
+ frappe.db.set_value(
+ "Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
+ )
+
+ to_delete = [
+ "Test Group Account - _TC3",
+ "Test Group Account - _TC4",
+ "Test Modified Account - _TC5",
+ ]
for doc in to_delete:
frappe.delete_doc("Account", doc)
+ def test_validate_account_currency(self):
+ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+
+ if not frappe.db.get_value("Account", "Test Currency Account - _TC"):
+ acc = frappe.new_doc("Account")
+ acc.account_name = "Test Currency Account"
+ acc.parent_account = "Tax Assets - _TC"
+ acc.company = "_Test Company"
+ acc.insert()
+ else:
+ acc = frappe.get_doc("Account", "Test Currency Account - _TC")
+
+ 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
+ )
+
+ acc.account_currency = "USD"
+ self.assertRaises(frappe.ValidationError, acc.save)
+
def _make_test_records(verbose=None):
from frappe.test_runner import make_test_objects
@@ -184,20 +273,16 @@ def _make_test_records(verbose=None):
["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"],
["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"],
["_Test Cash", "Cash In Hand", 0, "Cash", None],
-
["_Test Account Stock Expenses", "Direct Expenses", 1, None, None],
["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None],
["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None],
["_Test Employee Advance", "Current Liabilities", 0, None, None],
-
["_Test Account Tax Assets", "Current Assets", 1, None, None],
["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax", None],
-
["_Test Account Reserves and Surplus", "Current Liabilities", 0, None, None],
-
["_Test Account Cost for Goods Sold", "Expenses", 0, None, None],
["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax", None],
@@ -206,38 +291,45 @@ def _make_test_records(verbose=None):
["_Test Account Discount", "Direct Expenses", 0, None, None],
["_Test Write Off", "Indirect Expenses", 0, None, None],
["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None],
-
["_Test Account Sales", "Direct Income", 0, None, None],
-
# related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
-
# fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
["_Test Depreciations", "Expenses", 0, None, None],
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
-
# Receivable / Payable Account
["_Test Receivable", "Current Assets", 0, "Receivable", None],
["_Test Payable", "Current Liabilities", 0, "Payable", None],
["_Test Receivable USD", "Current Assets", 0, "Receivable", "USD"],
- ["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
+ ["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"],
]
- for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"], ["_Test Company with perpetual inventory", "TCP1"]]:
- test_objects = make_test_objects("Account", [{
- "doctype": "Account",
- "account_name": account_name,
- "parent_account": parent_account + " - " + abbr,
- "company": company,
- "is_group": is_group,
- "account_type": account_type,
- "account_currency": currency
- } for account_name, parent_account, is_group, account_type, currency in accounts])
+ for company, abbr in [
+ ["_Test Company", "_TC"],
+ ["_Test Company 1", "_TC1"],
+ ["_Test Company with perpetual inventory", "TCP1"],
+ ]:
+ test_objects = make_test_objects(
+ "Account",
+ [
+ {
+ "doctype": "Account",
+ "account_name": account_name,
+ "parent_account": parent_account + " - " + abbr,
+ "company": company,
+ "is_group": is_group,
+ "account_type": account_type,
+ "account_currency": currency,
+ }
+ for account_name, parent_account, is_group, account_type, currency in accounts
+ ],
+ )
return test_objects
+
def get_inventory_account(company, warehouse=None):
account = None
if warehouse:
@@ -247,19 +339,24 @@ def get_inventory_account(company, warehouse=None):
return account
+
def create_account(**kwargs):
- account = frappe.db.get_value("Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")})
+ account = frappe.db.get_value(
+ "Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")}
+ )
if account:
return account
else:
- account = frappe.get_doc(dict(
- doctype = "Account",
- account_name = kwargs.get('account_name'),
- account_type = kwargs.get('account_type'),
- parent_account = kwargs.get('parent_account'),
- company = kwargs.get('company'),
- account_currency = kwargs.get('account_currency')
- ))
+ account = frappe.get_doc(
+ dict(
+ doctype="Account",
+ account_name=kwargs.get("account_name"),
+ account_type=kwargs.get("account_type"),
+ parent_account=kwargs.get("parent_account"),
+ company=kwargs.get("company"),
+ account_currency=kwargs.get("account_currency"),
+ )
+ )
account.save()
return account.name
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index b6112e0cc576..897151a97b7f 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -17,13 +17,21 @@ def before_insert(self):
self.set_fieldname_and_label()
def validate(self):
- if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
- 'Cost Center', 'Accounting Dimension Detail', 'Company', 'Account') :
+ if self.document_type in core_doctypes_list + (
+ "Accounting Dimension",
+ "Project",
+ "Cost Center",
+ "Accounting Dimension Detail",
+ "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"))
@@ -42,13 +50,13 @@ def after_insert(self):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
else:
- frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue='long')
+ frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue="long")
def on_trash(self):
if frappe.flags.in_test:
delete_accounting_dimension(doc=self)
else:
- frappe.enqueue(delete_accounting_dimension, doc=self, queue='long')
+ frappe.enqueue(delete_accounting_dimension, doc=self, queue="long")
def set_fieldname_and_label(self):
if not self.label:
@@ -60,6 +68,7 @@ def set_fieldname_and_label(self):
def on_update(self):
frappe.flags.accounting_dimensions = None
+
def make_dimension_in_accounting_doctypes(doc, doclist=None):
if not doclist:
doclist = get_doctypes_with_dimensions()
@@ -70,9 +79,9 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
for doctype in doclist:
if (doc_count + 1) % 2 == 0:
- insert_after_field = 'dimension_col_break'
+ insert_after_field = "dimension_col_break"
else:
- insert_after_field = 'accounting_dimensions_section'
+ insert_after_field = "accounting_dimensions_section"
df = {
"fieldname": doc.fieldname,
@@ -80,13 +89,13 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
"fieldtype": "Link",
"options": doc.document_type,
"insert_after": insert_after_field,
- "owner": "Administrator"
+ "owner": "Administrator",
}
meta = frappe.get_meta(doctype, cached=False)
fieldnames = [d.fieldname for d in meta.get("fields")]
- if df['fieldname'] not in fieldnames:
+ if df["fieldname"] not in fieldnames:
if doctype == "Budget":
add_dimension_to_budget_doctype(df.copy(), doc)
else:
@@ -94,14 +103,17 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
count += 1
- frappe.publish_progress(count*100/len(doclist), title = _("Creating Dimensions..."))
+ frappe.publish_progress(count * 100 / len(doclist), title=_("Creating Dimensions..."))
frappe.clear_cache(doctype=doctype)
+
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)
- })
+ df.update(
+ {
+ "insert_after": "cost_center",
+ "depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type),
+ }
+ )
create_custom_field("Budget", df)
@@ -112,36 +124,44 @@ def add_dimension_to_budget_doctype(df, doc):
property_setter_doc.value = property_setter_doc.value + "\n" + doc.document_type
property_setter_doc.save()
- frappe.clear_cache(doctype='Budget')
+ frappe.clear_cache(doctype="Budget")
else:
- frappe.get_doc({
- "doctype": "Property Setter",
- "doctype_or_field": "DocField",
- "doc_type": "Budget",
- "field_name": "budget_against",
- "property": "options",
- "property_type": "Text",
- "value": "\nCost Center\nProject\n" + doc.document_type
- }).insert(ignore_permissions=True)
+ frappe.get_doc(
+ {
+ "doctype": "Property Setter",
+ "doctype_or_field": "DocField",
+ "doc_type": "Budget",
+ "field_name": "budget_against",
+ "property": "options",
+ "property_type": "Text",
+ "value": "\nCost Center\nProject\n" + doc.document_type,
+ }
+ ).insert(ignore_permissions=True)
def delete_accounting_dimension(doc):
doclist = get_doctypes_with_dimensions()
- frappe.db.sql("""
+ frappe.db.sql(
+ """
DELETE FROM `tabCustom Field`
WHERE fieldname = %s
- AND dt IN (%s)""" % #nosec
- ('%s', ', '.join(['%s']* len(doclist))), tuple([doc.fieldname] + doclist))
+ AND dt IN (%s)"""
+ % ("%s", ", ".join(["%s"] * len(doclist))), # nosec
+ tuple([doc.fieldname] + doclist),
+ )
- frappe.db.sql("""
+ frappe.db.sql(
+ """
DELETE FROM `tabProperty Setter`
WHERE field_name = %s
- AND doc_type IN (%s)""" % #nosec
- ('%s', ', '.join(['%s']* len(doclist))), tuple([doc.fieldname] + doclist))
+ AND doc_type IN (%s)"""
+ % ("%s", ", ".join(["%s"] * len(doclist))), # nosec
+ tuple([doc.fieldname] + doclist),
+ )
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
- value_list = budget_against_property.value.split('\n')[3:]
+ value_list = budget_against_property.value.split("\n")[3:]
if doc.document_type in value_list:
value_list.remove(doc.document_type)
@@ -152,6 +172,7 @@ def delete_accounting_dimension(doc):
for doctype in doclist:
frappe.clear_cache(doctype=doctype)
+
@frappe.whitelist()
def disable_dimension(doc):
if frappe.flags.in_test:
@@ -159,10 +180,11 @@ def disable_dimension(doc):
else:
frappe.enqueue(toggle_disabling, doc=doc)
+
def toggle_disabling(doc):
doc = json.loads(doc)
- if doc.get('disabled'):
+ if doc.get("disabled"):
df = {"read_only": 1}
else:
df = {"read_only": 0}
@@ -170,7 +192,7 @@ def toggle_disabling(doc):
doclist = get_doctypes_with_dimensions()
for doctype in doclist:
- field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": doc.get('fieldname')})
+ field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": doc.get("fieldname")})
if field:
custom_field = frappe.get_doc("Custom Field", field)
custom_field.update(df)
@@ -178,26 +200,34 @@ def toggle_disabling(doc):
frappe.clear_cache(doctype=doctype)
+
def get_doctypes_with_dimensions():
return frappe.get_hooks("accounting_dimension_doctypes")
+
def get_accounting_dimensions(as_list=True):
if frappe.flags.accounting_dimensions is None:
- frappe.flags.accounting_dimensions = frappe.get_all("Accounting Dimension",
- fields=["label", "fieldname", "disabled", "document_type"])
+ frappe.flags.accounting_dimensions = frappe.get_all(
+ "Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"]
+ )
if as_list:
return [d.fieldname for d in frappe.flags.accounting_dimensions]
else:
return frappe.flags.accounting_dimensions
+
def get_checks_for_pl_and_bs_accounts():
- dimensions = frappe.db.sql("""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
+ dimensions = frappe.db.sql(
+ """SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
- WHERE p.name = c.parent""", as_dict=1)
+ WHERE p.name = c.parent""",
+ as_dict=1,
+ )
return dimensions
+
def get_dimension_with_children(doctype, dimension):
if isinstance(dimension, list):
@@ -205,34 +235,39 @@ def get_dimension_with_children(doctype, dimension):
all_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
+
@frappe.whitelist()
def get_dimensions(with_cost_center_and_project=False):
- dimension_filters = frappe.db.sql("""
+ dimension_filters = frappe.db.sql(
+ """
SELECT label, fieldname, document_type
FROM `tabAccounting Dimension`
WHERE disabled = 0
- """, as_dict=1)
+ """,
+ as_dict=1,
+ )
- default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension
+ default_dimensions = frappe.db.sql(
+ """SELECT p.fieldname, c.company, c.default_dimension
FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
- WHERE c.parent = p.name""", as_dict=1)
+ WHERE c.parent = p.name""",
+ as_dict=1,
+ )
if with_cost_center_and_project:
- dimension_filters.extend([
- {
- 'fieldname': 'cost_center',
- 'document_type': 'Cost Center'
- },
- {
- 'fieldname': 'project',
- 'document_type': 'Project'
- }
- ])
+ dimension_filters.extend(
+ [
+ {"fieldname": "cost_center", "document_type": "Cost Center"},
+ {"fieldname": "project", "document_type": "Project"},
+ ]
+ )
default_dimensions_map = {}
for dimension in default_dimensions:
diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
index f781a221ddff..25ef2ea5c2c2 100644
--- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
@@ -8,7 +8,8 @@
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-test_dependencies = ['Cost Center', 'Location', 'Warehouse', 'Department']
+test_dependencies = ["Cost Center", "Location", "Warehouse", "Department"]
+
class TestAccountingDimension(unittest.TestCase):
def setUp(self):
@@ -18,24 +19,27 @@ def test_dimension_against_sales_invoice(self):
si = create_sales_invoice(do_not_save=1)
si.location = "Block 1"
- si.append("items", {
- "item_code": "_Test Item",
- "warehouse": "_Test Warehouse - _TC",
- "qty": 1,
- "rate": 100,
- "income_account": "Sales - _TC",
- "expense_account": "Cost of Goods Sold - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "department": "_Test Department - _TC",
- "location": "Block 1"
- })
+ si.append(
+ "items",
+ {
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC",
+ "qty": 1,
+ "rate": 100,
+ "income_account": "Sales - _TC",
+ "expense_account": "Cost of Goods Sold - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "department": "_Test Department - _TC",
+ "location": "Block 1",
+ },
+ )
si.save()
si.submit()
gle = frappe.get_doc("GL Entry", {"voucher_no": si.name, "account": "Sales - _TC"})
- self.assertEqual(gle.get('department'), "_Test Department - _TC")
+ self.assertEqual(gle.get("department"), "_Test Department - _TC")
def test_dimension_against_journal_entry(self):
je = make_journal_entry("Sales - _TC", "Sales Expenses - _TC", 500, save=False)
@@ -50,21 +54,24 @@ def test_dimension_against_journal_entry(self):
gle = frappe.get_doc("GL Entry", {"voucher_no": je.name, "account": "Sales - _TC"})
gle1 = frappe.get_doc("GL Entry", {"voucher_no": je.name, "account": "Sales Expenses - _TC"})
- self.assertEqual(gle.get('department'), "_Test Department - _TC")
- self.assertEqual(gle1.get('department'), "_Test Department - _TC")
+ self.assertEqual(gle.get("department"), "_Test Department - _TC")
+ self.assertEqual(gle1.get("department"), "_Test Department - _TC")
def test_mandatory(self):
si = create_sales_invoice(do_not_save=1)
- si.append("items", {
- "item_code": "_Test Item",
- "warehouse": "_Test Warehouse - _TC",
- "qty": 1,
- "rate": 100,
- "income_account": "Sales - _TC",
- "expense_account": "Cost of Goods Sold - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "location": ""
- })
+ si.append(
+ "items",
+ {
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC",
+ "qty": 1,
+ "rate": 100,
+ "income_account": "Sales - _TC",
+ "expense_account": "Cost of Goods Sold - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "location": "",
+ },
+ )
si.save()
self.assertRaises(frappe.ValidationError, si.submit)
@@ -72,31 +79,39 @@ def test_mandatory(self):
def tearDown(self):
disable_dimension()
+
def create_dimension():
frappe.set_user("Administrator")
if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}):
- frappe.get_doc({
- "doctype": "Accounting Dimension",
- "document_type": "Department",
- }).insert()
+ frappe.get_doc(
+ {
+ "doctype": "Accounting Dimension",
+ "document_type": "Department",
+ }
+ ).insert()
else:
dimension = frappe.get_doc("Accounting Dimension", "Department")
dimension.disabled = 0
dimension.save()
if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}):
- dimension1 = frappe.get_doc({
- "doctype": "Accounting Dimension",
- "document_type": "Location",
- })
-
- dimension1.append("dimension_defaults", {
- "company": "_Test Company",
- "reference_document": "Location",
- "default_dimension": "Block 1",
- "mandatory_for_bs": 1
- })
+ dimension1 = frappe.get_doc(
+ {
+ "doctype": "Accounting Dimension",
+ "document_type": "Location",
+ }
+ )
+
+ dimension1.append(
+ "dimension_defaults",
+ {
+ "company": "_Test Company",
+ "reference_document": "Location",
+ "default_dimension": "Block 1",
+ "mandatory_for_bs": 1,
+ },
+ )
dimension1.insert()
dimension1.save()
@@ -105,6 +120,7 @@ def create_dimension():
dimension1.disabled = 0
dimension1.save()
+
def disable_dimension():
dimension1 = frappe.get_doc("Accounting Dimension", "Department")
dimension1.disabled = 1
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
index 7d32bad0e783..80f736fa5bbe 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
@@ -19,17 +19,27 @@ def validate_applicable_accounts(self):
WHERE d.name = a.parent
and d.name != %s
and d.accounting_dimension = %s
- """, (self.name, self.accounting_dimension), as_dict=1)
+ """,
+ (self.name, self.accounting_dimension),
+ as_dict=1,
+ )
account_list = [d.account for d in accounts]
- for account in self.get('accounts'):
+ for account in self.get("accounts"):
if account.applicable_on_account in account_list:
- frappe.throw(_("Row {0}: {1} account already applied for Accounting Dimension {2}").format(
- account.idx, frappe.bold(account.applicable_on_account), frappe.bold(self.accounting_dimension)))
+ frappe.throw(
+ _("Row {0}: {1} account already applied for Accounting Dimension {2}").format(
+ account.idx,
+ frappe.bold(account.applicable_on_account),
+ frappe.bold(self.accounting_dimension),
+ )
+ )
+
def get_dimension_filter_map():
- filters = frappe.db.sql("""
+ filters = frappe.db.sql(
+ """
SELECT
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
p.allow_or_restrict, a.is_mandatory
@@ -40,22 +50,30 @@ def get_dimension_filter_map():
p.name = a.parent
AND p.disabled = 0
AND p.name = d.parent
- """, as_dict=1)
+ """,
+ as_dict=1,
+ )
dimension_filter_map = {}
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
- build_map(dimension_filter_map, f.fieldname, f.applicable_on_account, f.dimension_value,
- f.allow_or_restrict, f.is_mandatory)
+ build_map(
+ dimension_filter_map,
+ f.fieldname,
+ f.applicable_on_account,
+ f.dimension_value,
+ f.allow_or_restrict,
+ f.is_mandatory,
+ )
return dimension_filter_map
+
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
- map_object.setdefault((dimension, account), {
- 'allowed_dimensions': [],
- 'is_mandatory': is_mandatory,
- 'allow_or_restrict': allow_or_restrict
- })
- map_object[(dimension, account)]['allowed_dimensions'].append(filter_value)
+ map_object.setdefault(
+ (dimension, account),
+ {"allowed_dimensions": [], "is_mandatory": is_mandatory, "allow_or_restrict": allow_or_restrict},
+ )
+ map_object[(dimension, account)]["allowed_dimensions"].append(filter_value)
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
index e2f85ba21a9f..f13f2f9f2791 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
@@ -12,7 +12,8 @@
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
-test_dependencies = ['Location', 'Cost Center', 'Department']
+test_dependencies = ["Location", "Cost Center", "Department"]
+
class TestAccountingDimensionFilter(unittest.TestCase):
def setUp(self):
@@ -22,9 +23,9 @@ def setUp(self):
def test_allowed_dimension_validation(self):
si = create_sales_invoice(do_not_save=1)
- si.items[0].cost_center = 'Main - _TC'
- si.department = 'Accounts - _TC'
- si.location = 'Block 1'
+ si.items[0].cost_center = "Main - _TC"
+ si.department = "Accounts - _TC"
+ si.location = "Block 1"
si.save()
self.assertRaises(InvalidAccountDimensionError, si.submit)
@@ -32,12 +33,12 @@ def test_allowed_dimension_validation(self):
def test_mandatory_dimension_validation(self):
si = create_sales_invoice(do_not_save=1)
- si.department = ''
- si.location = 'Block 1'
+ si.department = ""
+ si.location = "Block 1"
# Test with no department for Sales Account
- si.items[0].department = ''
- si.items[0].cost_center = '_Test Cost Center 2 - _TC'
+ si.items[0].department = ""
+ si.items[0].cost_center = "_Test Cost Center 2 - _TC"
si.save()
self.assertRaises(MandatoryAccountDimensionError, si.submit)
@@ -52,53 +53,54 @@ def tearDown(self):
if si.docstatus == 1:
si.cancel()
+
def create_accounting_dimension_filter():
- if not frappe.db.get_value('Accounting Dimension Filter',
- {'accounting_dimension': 'Cost Center'}):
- frappe.get_doc({
- 'doctype': 'Accounting Dimension Filter',
- 'accounting_dimension': 'Cost Center',
- 'allow_or_restrict': 'Allow',
- 'company': '_Test Company',
- 'accounts': [{
- 'applicable_on_account': 'Sales - _TC',
- }],
- 'dimensions': [{
- 'accounting_dimension': 'Cost Center',
- 'dimension_value': '_Test Cost Center 2 - _TC'
- }]
- }).insert()
+ if not frappe.db.get_value(
+ "Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
+ ):
+ frappe.get_doc(
+ {
+ "doctype": "Accounting Dimension Filter",
+ "accounting_dimension": "Cost Center",
+ "allow_or_restrict": "Allow",
+ "company": "_Test Company",
+ "accounts": [
+ {
+ "applicable_on_account": "Sales - _TC",
+ }
+ ],
+ "dimensions": [
+ {"accounting_dimension": "Cost Center", "dimension_value": "_Test Cost Center 2 - _TC"}
+ ],
+ }
+ ).insert()
else:
- doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
+ doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"})
doc.disabled = 0
doc.save()
- if not frappe.db.get_value('Accounting Dimension Filter',
- {'accounting_dimension': 'Department'}):
- frappe.get_doc({
- 'doctype': 'Accounting Dimension Filter',
- 'accounting_dimension': 'Department',
- 'allow_or_restrict': 'Allow',
- 'company': '_Test Company',
- 'accounts': [{
- 'applicable_on_account': 'Sales - _TC',
- 'is_mandatory': 1
- }],
- 'dimensions': [{
- 'accounting_dimension': 'Department',
- 'dimension_value': 'Accounts - _TC'
- }]
- }).insert()
+ if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Department"}):
+ frappe.get_doc(
+ {
+ "doctype": "Accounting Dimension Filter",
+ "accounting_dimension": "Department",
+ "allow_or_restrict": "Allow",
+ "company": "_Test Company",
+ "accounts": [{"applicable_on_account": "Sales - _TC", "is_mandatory": 1}],
+ "dimensions": [{"accounting_dimension": "Department", "dimension_value": "Accounts - _TC"}],
+ }
+ ).insert()
else:
- doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
+ doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Department"})
doc.disabled = 0
doc.save()
+
def disable_dimension_filter():
- doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
+ doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"})
doc.disabled = 1
doc.save()
- doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
+ doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Department"})
doc.disabled = 1
doc.save()
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index e2949378e5f7..0c15d6a207d7 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -7,7 +7,9 @@
from frappe.model.document import Document
-class OverlapError(frappe.ValidationError): pass
+class OverlapError(frappe.ValidationError):
+ pass
+
class AccountingPeriod(Document):
def validate(self):
@@ -17,11 +19,12 @@ def before_insert(self):
self.bootstrap_doctypes_for_closing()
def autoname(self):
- company_abbr = frappe.get_cached_value('Company', self.company, "abbr")
+ company_abbr = frappe.get_cached_value("Company", self.company, "abbr")
self.name = " - ".join([self.period_name, company_abbr])
def validate_overlap(self):
- existing_accounting_period = frappe.db.sql("""select name from `tabAccounting Period`
+ existing_accounting_period = frappe.db.sql(
+ """select name from `tabAccounting Period`
where (
(%(start_date)s between start_date and end_date)
or (%(end_date)s between start_date and end_date)
@@ -32,18 +35,29 @@ def validate_overlap(self):
"start_date": self.start_date,
"end_date": self.end_date,
"name": self.name,
- "company": self.company
- }, as_dict=True)
+ "company": self.company,
+ },
+ as_dict=True,
+ )
if len(existing_accounting_period) > 0:
- frappe.throw(_("Accounting Period overlaps with {0}")
- .format(existing_accounting_period[0].get("name")), OverlapError)
+ frappe.throw(
+ _("Accounting Period overlaps with {0}").format(existing_accounting_period[0].get("name")),
+ OverlapError,
+ )
@frappe.whitelist()
def get_doctypes_for_closing(self):
docs_for_closing = []
- doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
- "Bank Clearance", "Asset", "Stock Entry"]
+ doctypes = [
+ "Sales Invoice",
+ "Purchase Invoice",
+ "Journal Entry",
+ "Payroll Entry",
+ "Bank Clearance",
+ "Asset",
+ "Stock Entry",
+ ]
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
for closed_doctype in closed_doctypes:
docs_for_closing.append(closed_doctype)
@@ -53,7 +67,7 @@ def get_doctypes_for_closing(self):
def bootstrap_doctypes_for_closing(self):
if len(self.closed_documents) == 0:
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
- })
+ self.append(
+ "closed_documents",
+ {"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
+ )
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
index c06c2e0338b3..85025d190f5c 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -10,29 +10,38 @@
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
-test_dependencies = ['Item']
+test_dependencies = ["Item"]
+
class TestAccountingPeriod(unittest.TestCase):
def test_overlap(self):
- ap1 = create_accounting_period(start_date = "2018-04-01",
- end_date = "2018-06-30", company = "Wind Power LLC")
+ ap1 = create_accounting_period(
+ start_date="2018-04-01", end_date="2018-06-30", company="Wind Power LLC"
+ )
ap1.save()
- ap2 = create_accounting_period(start_date = "2018-06-30",
- end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
+ ap2 = create_accounting_period(
+ start_date="2018-06-30",
+ end_date="2018-07-10",
+ company="Wind Power LLC",
+ period_name="Test Accounting Period 1",
+ )
self.assertRaises(OverlapError, ap2.save)
def test_accounting_period(self):
- ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
+ ap1 = create_accounting_period(period_name="Test Accounting Period 2")
ap1.save()
- doc = create_sales_invoice(do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
+ doc = create_sales_invoice(
+ do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
+ )
self.assertRaises(ClosedAccountingPeriod, doc.submit)
def tearDown(self):
for d in frappe.get_all("Accounting Period"):
frappe.delete_doc("Accounting Period", d.name)
+
def create_accounting_period(**args):
args = frappe._dict(args)
@@ -41,8 +50,6 @@ def create_accounting_period(**args):
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
accounting_period.company = args.company or "_Test Company"
accounting_period.period_name = args.period_name or "_Test_Period_Name_1"
- accounting_period.append("closed_documents", {
- "document_type": 'Sales Invoice', "closed": 1
- })
+ accounting_period.append("closed_documents", {"document_type": "Sales Invoice", "closed": 1})
return accounting_period
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
index 483920741026..835498176c79 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -18,11 +18,13 @@ def on_update(self):
frappe.clear_cache()
def validate(self):
- frappe.db.set_default("add_taxes_from_item_tax_template",
- self.get("add_taxes_from_item_tax_template", 0))
+ frappe.db.set_default(
+ "add_taxes_from_item_tax_template", self.get("add_taxes_from_item_tax_template", 0)
+ )
- frappe.db.set_default("enable_common_party_accounting",
- self.get("enable_common_party_accounting", 0))
+ frappe.db.set_default(
+ "enable_common_party_accounting", self.get("enable_common_party_accounting", 0)
+ )
self.validate_stale_days()
self.enable_payment_schedule_in_print()
@@ -32,34 +34,91 @@ def validate(self):
def validate_stale_days(self):
if not self.allow_stale and cint(self.stale_days) <= 0:
frappe.msgprint(
- _("Stale Days should start from 1."), title='Error', indicator='red',
- raise_exception=1)
+ _("Stale Days should start from 1."), title="Error", indicator="red", raise_exception=1
+ )
def enable_payment_schedule_in_print(self):
show_in_print = cint(self.show_payment_schedule_in_print)
for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
- make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False)
- make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check", validate_fields_for_doctype=False)
+ make_property_setter(
+ doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False
+ )
+ make_property_setter(
+ doctype,
+ "payment_schedule",
+ "print_hide",
+ 0 if show_in_print else 1,
+ "Check",
+ validate_fields_for_doctype=False,
+ )
def toggle_discount_accounting_fields(self):
enable_discount_accounting = cint(self.enable_discount_accounting)
for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
- make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
+ make_property_setter(
+ doctype,
+ "discount_account",
+ "hidden",
+ not (enable_discount_accounting),
+ "Check",
+ validate_fields_for_doctype=False,
+ )
if enable_discount_accounting:
- make_property_setter(doctype, "discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
+ make_property_setter(
+ doctype,
+ "discount_account",
+ "mandatory_depends_on",
+ "eval: doc.discount_amount",
+ "Code",
+ validate_fields_for_doctype=False,
+ )
else:
- make_property_setter(doctype, "discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
+ make_property_setter(
+ doctype,
+ "discount_account",
+ "mandatory_depends_on",
+ "",
+ "Code",
+ validate_fields_for_doctype=False,
+ )
for doctype in ["Sales Invoice", "Purchase Invoice"]:
- make_property_setter(doctype, "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
+ make_property_setter(
+ doctype,
+ "additional_discount_account",
+ "hidden",
+ not (enable_discount_accounting),
+ "Check",
+ validate_fields_for_doctype=False,
+ )
if enable_discount_accounting:
- make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
+ make_property_setter(
+ doctype,
+ "additional_discount_account",
+ "mandatory_depends_on",
+ "eval: doc.discount_amount",
+ "Code",
+ validate_fields_for_doctype=False,
+ )
else:
- make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
-
- make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
-
+ make_property_setter(
+ doctype,
+ "additional_discount_account",
+ "mandatory_depends_on",
+ "",
+ "Code",
+ validate_fields_for_doctype=False,
+ )
+
+ make_property_setter(
+ "Item",
+ "default_discount_account",
+ "hidden",
+ not (enable_discount_accounting),
+ "Check",
+ validate_fields_for_doctype=False,
+ )
def validate_pending_reposts(self):
if self.acc_frozen_upto:
diff --git a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
index bf1e967bdbcc..a350cc385da7 100644
--- a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
@@ -7,12 +7,12 @@ class TestAccountsSettings(unittest.TestCase):
def tearDown(self):
# Just in case `save` method succeeds, we need to take things back to default so that other tests
# don't break
- cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+ cur_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
cur_settings.allow_stale = 1
cur_settings.save()
def test_stale_days(self):
- cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+ cur_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
cur_settings.allow_stale = 0
cur_settings.stale_days = 0
diff --git a/erpnext/accounts/doctype/bank/bank.py b/erpnext/accounts/doctype/bank/bank.py
index f111433c321e..d44be9af23ec 100644
--- a/erpnext/accounts/doctype/bank/bank.py
+++ b/erpnext/accounts/doctype/bank/bank.py
@@ -15,4 +15,4 @@ def onload(self):
load_address_and_contact(self)
def on_trash(self):
- delete_contact_and_address('Bank', self.name)
+ delete_contact_and_address("Bank", self.name)
diff --git a/erpnext/accounts/doctype/bank/bank_dashboard.py b/erpnext/accounts/doctype/bank/bank_dashboard.py
index 36482aac9600..7e40a1a6b8e9 100644
--- a/erpnext/accounts/doctype/bank/bank_dashboard.py
+++ b/erpnext/accounts/doctype/bank/bank_dashboard.py
@@ -3,11 +3,6 @@
def get_data():
return {
- 'fieldname': 'bank',
- 'transactions': [
- {
- 'label': _('Bank Details'),
- 'items': ['Bank Account', 'Bank Guarantee']
- }
- ]
+ "fieldname": "bank",
+ "transactions": [{"label": _("Bank Details"), "items": ["Bank Account", "Bank Guarantee"]}],
}
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py
index f9140c31d647..addcf62e5b60 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -20,7 +20,7 @@ def autoname(self):
self.name = self.account_name + " - " + self.bank
def on_trash(self):
- delete_contact_and_address('BankAccount', self.name)
+ delete_contact_and_address("BankAccount", self.name)
def validate(self):
self.validate_company()
@@ -31,9 +31,9 @@ def validate_company(self):
frappe.throw(_("Company is manadatory for company account"))
def validate_iban(self):
- '''
+ """
Algorithm: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN
- '''
+ """
# IBAN field is optional
if not self.iban:
return
@@ -43,7 +43,7 @@ def encode_char(c):
return str(9 + ord(c) - 64)
# remove whitespaces, upper case to get the right number from ord()
- iban = ''.join(self.iban.split(' ')).upper()
+ iban = "".join(self.iban.split(" ")).upper()
# Move country code and checksum from the start to the end
flipped = iban[4:] + iban[:4]
@@ -52,12 +52,12 @@ def encode_char(c):
encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped]
try:
- to_check = int(''.join(encoded))
+ to_check = int("".join(encoded))
except ValueError:
- frappe.throw(_('IBAN is not valid'))
+ frappe.throw(_("IBAN is not valid"))
if to_check % 97 != 1:
- frappe.throw(_('IBAN is not valid'))
+ frappe.throw(_("IBAN is not valid"))
@frappe.whitelist()
@@ -69,12 +69,14 @@ def make_bank_account(doctype, docname):
return doc
+
@frappe.whitelist()
def get_party_bank_account(party_type, party):
- return frappe.db.get_value(party_type,
- party, 'default_bank_account')
+ return frappe.db.get_value(party_type, party, "default_bank_account")
+
@frappe.whitelist()
def get_bank_account_details(bank_account):
- return frappe.db.get_value("Bank Account",
- bank_account, ['account', 'bank', 'bank_account_no'], as_dict=1)
+ return frappe.db.get_value(
+ "Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
+ )
diff --git a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
index db4d7e51d594..8bf8d8a5cd01 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
@@ -3,25 +3,18 @@
def get_data():
return {
- 'fieldname': 'bank_account',
- 'non_standard_fieldnames': {
- 'Customer': 'default_bank_account',
- 'Supplier': 'default_bank_account',
+ "fieldname": "bank_account",
+ "non_standard_fieldnames": {
+ "Customer": "default_bank_account",
+ "Supplier": "default_bank_account",
},
- 'transactions': [
+ "transactions": [
{
- 'label': _('Payments'),
- 'items': ['Payment Entry', 'Payment Request', 'Payment Order', 'Payroll Entry']
+ "label": _("Payments"),
+ "items": ["Payment Entry", "Payment Request", "Payment Order", "Payroll Entry"],
},
- {
- 'label': _('Party'),
- 'items': ['Customer', 'Supplier']
- },
- {
- 'items': ['Bank Guarantee']
- },
- {
- 'items': ['Journal Entry']
- }
- ]
+ {"label": _("Party"), "items": ["Customer", "Supplier"]},
+ {"items": ["Bank Guarantee"]},
+ {"items": ["Journal Entry"]},
+ ],
}
diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.py b/erpnext/accounts/doctype/bank_account/test_bank_account.py
index 5f23f88af6ce..8949524a561d 100644
--- a/erpnext/accounts/doctype/bank_account/test_bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py
@@ -8,28 +8,28 @@
# test_records = frappe.get_test_records('Bank Account')
-class TestBankAccount(unittest.TestCase):
+class TestBankAccount(unittest.TestCase):
def test_validate_iban(self):
valid_ibans = [
- 'GB82 WEST 1234 5698 7654 32',
- 'DE91 1000 0000 0123 4567 89',
- 'FR76 3000 6000 0112 3456 7890 189'
+ "GB82 WEST 1234 5698 7654 32",
+ "DE91 1000 0000 0123 4567 89",
+ "FR76 3000 6000 0112 3456 7890 189",
]
invalid_ibans = [
# wrong checksum (3rd place)
- 'GB72 WEST 1234 5698 7654 32',
- 'DE81 1000 0000 0123 4567 89',
- 'FR66 3000 6000 0112 3456 7890 189'
+ "GB72 WEST 1234 5698 7654 32",
+ "DE81 1000 0000 0123 4567 89",
+ "FR66 3000 6000 0112 3456 7890 189",
]
- bank_account = frappe.get_doc({'doctype':'Bank Account'})
+ bank_account = frappe.get_doc({"doctype": "Bank Account"})
try:
bank_account.validate_iban()
except AttributeError:
- msg = 'BankAccount.validate_iban() failed for empty IBAN'
+ msg = "BankAccount.validate_iban() failed for empty IBAN"
self.fail(msg=msg)
for iban in valid_ibans:
@@ -37,11 +37,11 @@ def test_validate_iban(self):
try:
bank_account.validate_iban()
except ValidationError:
- msg = 'BankAccount.validate_iban() failed for valid IBAN {}'.format(iban)
+ msg = "BankAccount.validate_iban() failed for valid IBAN {}".format(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 = "BankAccount.validate_iban() accepted invalid IBAN {}".format(not_iban)
with self.assertRaises(ValidationError, msg=msg):
bank_account.validate_iban()
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index a3bbb2288d38..96779d75be33 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -7,9 +7,8 @@
from frappe.model.document import Document
from frappe.utils import flt, fmt_money, getdate, nowdate
-form_grid_templates = {
- "journal_entries": "templates/form_grid/bank_reconciliation_grid.html"
-}
+form_grid_templates = {"journal_entries": "templates/form_grid/bank_reconciliation_grid.html"}
+
class BankClearance(Document):
@frappe.whitelist()
@@ -24,7 +23,8 @@ def get_payment_entries(self):
if not self.include_reconciled_entries:
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
- journal_entries = frappe.db.sql("""
+ journal_entries = frappe.db.sql(
+ """
select
"Journal Entry" as payment_document, t1.name as payment_entry,
t1.cheque_no as cheque_number, t1.cheque_date,
@@ -38,12 +38,18 @@ def get_payment_entries(self):
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": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
+ """.format(
+ condition=condition
+ ),
+ {"account": self.account, "from": self.from_date, "to": self.to_date},
+ as_dict=1,
+ )
if self.bank_account:
- condition += 'and bank_account = %(bank_account)s'
+ condition += "and bank_account = %(bank_account)s"
- payment_entries = frappe.db.sql("""
+ payment_entries = frappe.db.sql(
+ """
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date,
@@ -58,12 +64,22 @@ def get_payment_entries(self):
{condition}
order by
posting_date ASC, name DESC
- """.format(condition=condition), {"account": self.account, "from":self.from_date,
- "to": self.to_date, "bank_account": self.bank_account}, as_dict=1)
+ """.format(
+ condition=condition
+ ),
+ {
+ "account": self.account,
+ "from": self.from_date,
+ "to": self.to_date,
+ "bank_account": self.bank_account,
+ },
+ as_dict=1,
+ )
pos_sales_invoices, pos_purchase_invoices = [], []
if self.include_pos_transactions:
- pos_sales_invoices = frappe.db.sql("""
+ pos_sales_invoices = frappe.db.sql(
+ """
select
"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
si.posting_date, si.customer as against_account, sip.clearance_date,
@@ -74,9 +90,13 @@ def get_payment_entries(self):
and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s
order by
si.posting_date ASC, si.name DESC
- """, {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1)
+ """,
+ {"account": self.account, "from": self.from_date, "to": self.to_date},
+ as_dict=1,
+ )
- pos_purchase_invoices = frappe.db.sql("""
+ pos_purchase_invoices = frappe.db.sql(
+ """
select
"Purchase Invoice" as payment_document, pi.name as payment_entry, pi.paid_amount as credit,
pi.posting_date, pi.supplier as against_account, pi.clearance_date,
@@ -87,18 +107,24 @@ def get_payment_entries(self):
and pi.posting_date >= %(from)s and pi.posting_date <= %(to)s
order by
pi.posting_date ASC, pi.name DESC
- """, {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
-
- entries = sorted(list(payment_entries) + list(journal_entries + list(pos_sales_invoices) + list(pos_purchase_invoices)),
- key=lambda k: k['posting_date'] or getdate(nowdate()))
-
- self.set('payment_entries', [])
+ """,
+ {"account": self.account, "from": self.from_date, "to": self.to_date},
+ as_dict=1,
+ )
+
+ entries = sorted(
+ list(payment_entries)
+ + list(journal_entries + list(pos_sales_invoices) + list(pos_purchase_invoices)),
+ key=lambda k: k["posting_date"] or getdate(nowdate()),
+ )
+
+ self.set("payment_entries", [])
self.total_amount = 0.0
for d in entries:
- row = self.append('payment_entries', {})
+ row = self.append("payment_entries", {})
- amount = flt(d.get('debit', 0)) - flt(d.get('credit', 0))
+ amount = flt(d.get("debit", 0)) - flt(d.get("credit", 0))
formatted_amount = fmt_money(abs(amount), 2, d.account_currency)
d.amount = formatted_amount + " " + (_("Dr") if amount > 0 else _("Cr"))
@@ -112,21 +138,24 @@ def get_payment_entries(self):
@frappe.whitelist()
def update_clearance_date(self):
clearance_date_updated = False
- for d in self.get('payment_entries'):
+ for d in self.get("payment_entries"):
if d.clearance_date:
if not d.payment_document:
frappe.throw(_("Row #{0}: Payment document is required to complete the transaction"))
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
- frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
- .format(d.idx, d.clearance_date, d.cheque_date))
+ frappe.throw(
+ _("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format(
+ d.idx, d.clearance_date, d.cheque_date
+ )
+ )
if d.clearance_date or self.include_reconciled_entries:
if not d.clearance_date:
d.clearance_date = None
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
- payment_entry.db_set('clearance_date', d.clearance_date)
+ payment_entry.db_set("clearance_date", d.clearance_date)
clearance_date_updated = True
diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
index cfbcf16b91eb..9144a29c6ef6 100644
--- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
+++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
@@ -23,10 +23,16 @@ def on_submit(self):
if not self.bank:
frappe.throw(_("Enter the name of the bank or lending institution before submittting."))
+
@frappe.whitelist()
def get_vouchar_detials(column_list, doctype, docname):
column_list = json.loads(column_list)
for col in column_list:
sanitize_searchfield(col)
- return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
- .format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
+ return frappe.db.sql(
+ """ select {columns} from `tab{doctype}` where name=%s""".format(
+ columns=", ".join(column_list), doctype=doctype
+ ),
+ docname,
+ as_dict=1,
+ )[0]
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index f3351ddcba4c..4c25d7ccbe80 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -22,48 +22,63 @@
class BankReconciliationTool(Document):
pass
+
@frappe.whitelist()
-def get_bank_transactions(bank_account, from_date = None, to_date = None):
+def get_bank_transactions(bank_account, from_date=None, to_date=None):
# returns bank transactions for a bank account
filters = []
- filters.append(['bank_account', '=', bank_account])
- filters.append(['docstatus', '=', 1])
- filters.append(['unallocated_amount', '>', 0])
+ filters.append(["bank_account", "=", bank_account])
+ filters.append(["docstatus", "=", 1])
+ filters.append(["unallocated_amount", ">", 0])
if to_date:
- filters.append(['date', '<=', to_date])
+ filters.append(["date", "<=", to_date])
if from_date:
- filters.append(['date', '>=', from_date])
+ filters.append(["date", ">=", from_date])
transactions = frappe.get_all(
- 'Bank Transaction',
- fields = ['date', 'deposit', 'withdrawal', 'currency',
- 'description', 'name', 'bank_account', 'company',
- 'unallocated_amount', 'reference_number', 'party_type', 'party'],
- filters = filters
+ "Bank Transaction",
+ fields=[
+ "date",
+ "deposit",
+ "withdrawal",
+ "currency",
+ "description",
+ "name",
+ "bank_account",
+ "company",
+ "unallocated_amount",
+ "reference_number",
+ "party_type",
+ "party",
+ ],
+ filters=filters,
)
return transactions
+
@frappe.whitelist()
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
- })
+ account = frappe.db.get_value("Bank Account", bank_account, "account")
+ 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"])
- total_debit, total_credit = 0,0
+ total_debit, total_credit = 0, 0
for d in data:
total_debit += flt(d.debit)
total_credit += flt(d.credit)
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) \
+ bank_bal = (
+ flt(balance_as_per_system)
+ - flt(total_debit)
+ + flt(total_credit)
+ amounts_not_reflected_in_system
+ )
return bank_bal
@@ -76,71 +91,94 @@ def update_bank_transaction(bank_transaction_name, reference_number, party_type=
bank_transaction.party_type = party_type
bank_transaction.party = party
bank_transaction.save()
- return frappe.db.get_all('Bank Transaction',
- filters={
- 'name': bank_transaction_name
- },
- fields=['date', 'deposit', 'withdrawal', 'currency',
- 'description', 'name', 'bank_account', 'company',
- 'unallocated_amount', 'reference_number',
- 'party_type', 'party'],
+ return frappe.db.get_all(
+ "Bank Transaction",
+ filters={"name": bank_transaction_name},
+ fields=[
+ "date",
+ "deposit",
+ "withdrawal",
+ "currency",
+ "description",
+ "name",
+ "bank_account",
+ "company",
+ "unallocated_amount",
+ "reference_number",
+ "party_type",
+ "party",
+ ],
)[0]
@frappe.whitelist()
-def create_journal_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, posting_date=None, entry_type=None,
- second_account=None, mode_of_payment=None, party_type=None, party=None, allow_edit=None):
+def create_journal_entry_bts(
+ bank_transaction_name,
+ reference_number=None,
+ reference_date=None,
+ posting_date=None,
+ entry_type=None,
+ second_account=None,
+ mode_of_payment=None,
+ party_type=None,
+ party=None,
+ allow_edit=None,
+):
# Create a new journal entry based on the bank transaction
bank_transaction = frappe.db.get_values(
- "Bank Transaction", bank_transaction_name,
- fieldname=["name", "deposit", "withdrawal", "bank_account"] ,
- as_dict=True
+ "Bank Transaction",
+ bank_transaction_name,
+ fieldname=["name", "deposit", "withdrawal", "bank_account"],
+ as_dict=True,
)[0]
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
account_type = frappe.db.get_value("Account", second_account, "account_type")
if account_type in ["Receivable", "Payable"]:
if not (party_type and party):
- frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format( second_account))
+ frappe.throw(
+ _("Party Type and Party is required for Receivable / Payable account {0}").format(
+ second_account
+ )
+ )
accounts = []
# Multi Currency?
- accounts.append({
+ accounts.append(
+ {
"account": second_account,
- "credit_in_account_currency": bank_transaction.deposit
- if bank_transaction.deposit > 0
- else 0,
- "debit_in_account_currency":bank_transaction.withdrawal
- if bank_transaction.withdrawal > 0
- else 0,
- "party_type":party_type,
- "party":party,
- })
-
- accounts.append({
+ "credit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
+ "debit_in_account_currency": bank_transaction.withdrawal
+ if bank_transaction.withdrawal > 0
+ else 0,
+ "party_type": party_type,
+ "party": party,
+ }
+ )
+
+ accounts.append(
+ {
"account": company_account,
"bank_account": bank_transaction.bank_account,
"credit_in_account_currency": bank_transaction.withdrawal
- if bank_transaction.withdrawal > 0
- else 0,
- "debit_in_account_currency":bank_transaction.deposit
- if bank_transaction.deposit > 0
- else 0,
- })
+ if bank_transaction.withdrawal > 0
+ else 0,
+ "debit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
+ }
+ )
company = frappe.get_value("Account", company_account, "company")
journal_entry_dict = {
- "voucher_type" : entry_type,
- "company" : company,
- "posting_date" : posting_date,
- "cheque_date" : reference_date,
- "cheque_no" : reference_number,
- "mode_of_payment" : mode_of_payment
+ "voucher_type": entry_type,
+ "company": company,
+ "posting_date": posting_date,
+ "cheque_date": reference_date,
+ "cheque_no": reference_number,
+ "mode_of_payment": mode_of_payment,
}
- journal_entry = frappe.new_doc('Journal Entry')
+ journal_entry = frappe.new_doc("Journal Entry")
journal_entry.update(journal_entry_dict)
journal_entry.set("accounts", accounts)
-
if allow_edit:
return journal_entry
@@ -152,21 +190,32 @@ def create_journal_entry_bts( bank_transaction_name, reference_number=None, refe
else:
paid_amount = bank_transaction.withdrawal
- vouchers = json.dumps([{
- "payment_doctype":"Journal Entry",
- "payment_name":journal_entry.name,
- "amount":paid_amount}])
+ vouchers = json.dumps(
+ [{"payment_doctype": "Journal Entry", "payment_name": journal_entry.name, "amount": paid_amount}]
+ )
return reconcile_vouchers(bank_transaction.name, vouchers)
+
@frappe.whitelist()
-def create_payment_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, party_type=None, party=None, posting_date=None,
- mode_of_payment=None, project=None, cost_center=None, allow_edit=None):
+def create_payment_entry_bts(
+ bank_transaction_name,
+ reference_number=None,
+ reference_date=None,
+ party_type=None,
+ party=None,
+ posting_date=None,
+ mode_of_payment=None,
+ project=None,
+ cost_center=None,
+ allow_edit=None,
+):
# Create a new payment entry based on the bank transaction
bank_transaction = frappe.db.get_values(
- "Bank Transaction", bank_transaction_name,
- fieldname=["name", "unallocated_amount", "deposit", "bank_account"] ,
- as_dict=True
+ "Bank Transaction",
+ bank_transaction_name,
+ fieldname=["name", "unallocated_amount", "deposit", "bank_account"],
+ as_dict=True,
)[0]
paid_amount = bank_transaction.unallocated_amount
payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
@@ -174,27 +223,26 @@ def create_payment_entry_bts( bank_transaction_name, reference_number=None, refe
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
company = frappe.get_value("Account", company_account, "company")
payment_entry_dict = {
- "company" : company,
- "payment_type" : payment_type,
- "reference_no" : reference_number,
- "reference_date" : reference_date,
- "party_type" : party_type,
- "party" : party,
- "posting_date" : posting_date,
+ "company": company,
+ "payment_type": payment_type,
+ "reference_no": reference_number,
+ "reference_date": reference_date,
+ "party_type": party_type,
+ "party": party,
+ "posting_date": posting_date,
"paid_amount": paid_amount,
- "received_amount": paid_amount
+ "received_amount": paid_amount,
}
payment_entry = frappe.new_doc("Payment Entry")
-
payment_entry.update(payment_entry_dict)
if mode_of_payment:
- payment_entry.mode_of_payment = mode_of_payment
+ payment_entry.mode_of_payment = mode_of_payment
if project:
- payment_entry.project = project
+ payment_entry.project = project
if cost_center:
- payment_entry.cost_center = cost_center
+ payment_entry.cost_center = cost_center
if payment_type == "Receive":
payment_entry.paid_to = company_account
else:
@@ -208,84 +256,111 @@ def create_payment_entry_bts( bank_transaction_name, reference_number=None, refe
payment_entry.insert()
payment_entry.submit()
- vouchers = json.dumps([{
- "payment_doctype":"Payment Entry",
- "payment_name":payment_entry.name,
- "amount":paid_amount}])
+ vouchers = json.dumps(
+ [{"payment_doctype": "Payment Entry", "payment_name": payment_entry.name, "amount": paid_amount}]
+ )
return reconcile_vouchers(bank_transaction.name, vouchers)
+
@frappe.whitelist()
def reconcile_vouchers(bank_transaction_name, vouchers):
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
- company_account = frappe.db.get_value('Bank Account', transaction.bank_account, 'account')
+ company_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled"))
total_amount = 0
for voucher in vouchers:
- voucher['payment_entry'] = frappe.get_doc(voucher['payment_doctype'], voucher['payment_name'])
- total_amount += get_paid_amount(frappe._dict({
- 'payment_document': voucher['payment_doctype'],
- 'payment_entry': voucher['payment_name'],
- }), transaction.currency, company_account)
+ voucher["payment_entry"] = frappe.get_doc(voucher["payment_doctype"], voucher["payment_name"])
+ total_amount += get_paid_amount(
+ frappe._dict(
+ {
+ "payment_document": voucher["payment_doctype"],
+ "payment_entry": voucher["payment_name"],
+ }
+ ),
+ transaction.currency,
+ company_account,
+ )
if total_amount > transaction.unallocated_amount:
- frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
+ frappe.throw(
+ _(
+ "The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction"
+ )
+ )
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
for voucher in vouchers:
- gl_entry = frappe.db.get_value("GL Entry", dict(account=account, voucher_type=voucher['payment_doctype'], voucher_no=voucher['payment_name']), ['credit', 'debit'], as_dict=1)
- gl_amount, transaction_amount = (gl_entry.credit, transaction.deposit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.withdrawal)
+ gl_entry = frappe.db.get_value(
+ "GL Entry",
+ dict(
+ account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
+ ),
+ ["credit", "debit"],
+ as_dict=1,
+ )
+ gl_amount, transaction_amount = (
+ (gl_entry.credit, transaction.deposit)
+ if gl_entry.credit > 0
+ else (gl_entry.debit, transaction.withdrawal)
+ )
allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
- transaction.append("payment_entries", {
- "payment_document": voucher['payment_entry'].doctype,
- "payment_entry": voucher['payment_entry'].name,
- "allocated_amount": allocated_amount
- })
+ transaction.append(
+ "payment_entries",
+ {
+ "payment_document": voucher["payment_entry"].doctype,
+ "payment_entry": voucher["payment_entry"].name,
+ "allocated_amount": allocated_amount,
+ },
+ )
transaction.save()
transaction.update_allocations()
return frappe.get_doc("Bank Transaction", bank_transaction_name)
+
@frappe.whitelist()
-def get_linked_payments(bank_transaction_name, document_types = None):
+def get_linked_payments(bank_transaction_name, document_types=None):
# get all matching payments for a bank transaction
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_account = frappe.db.get_values(
- "Bank Account",
- transaction.bank_account,
- ["account", "company"],
- as_dict=True)[0]
+ "Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
+ )[0]
(account, company) = (bank_account.account, bank_account.company)
matching = check_matching(account, company, transaction, document_types)
return matching
+
def check_matching(bank_account, company, transaction, document_types):
# combine all types of vouchers
subquery = get_queries(bank_account, company, transaction, document_types)
filters = {
- "amount": transaction.unallocated_amount,
- "payment_type" : "Receive" if transaction.deposit > 0 else "Pay",
- "reference_no": transaction.reference_number,
- "party_type": transaction.party_type,
- "party": transaction.party,
- "bank_account": bank_account
- }
+ "amount": transaction.unallocated_amount,
+ "payment_type": "Receive" if transaction.deposit > 0 else "Pay",
+ "reference_no": transaction.reference_number,
+ "party_type": transaction.party_type,
+ "party": transaction.party,
+ "bank_account": bank_account,
+ }
matching_vouchers = []
- matching_vouchers.extend(get_loan_vouchers(bank_account, transaction,
- document_types, filters))
+ matching_vouchers.extend(get_loan_vouchers(bank_account, transaction, document_types, filters))
for query in subquery:
matching_vouchers.extend(
- frappe.db.sql(query, filters,)
+ frappe.db.sql(
+ query,
+ filters,
+ )
)
- return sorted(matching_vouchers, key = lambda x: x[0], reverse=True) if matching_vouchers else []
+ return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
+
def get_queries(bank_account, company, transaction, document_types):
# get queries to get matching vouchers
@@ -302,7 +377,7 @@ def get_queries(bank_account, company, transaction, document_types):
queries.extend([je_amount_matching])
if transaction.deposit > 0 and "sales_invoice" in document_types:
- si_amount_matching = get_si_matching_query(amount_condition)
+ si_amount_matching = get_si_matching_query(amount_condition)
queries.extend([si_amount_matching])
if transaction.withdrawal > 0:
@@ -316,6 +391,7 @@ def get_queries(bank_account, company, transaction, document_types):
return queries
+
def get_loan_vouchers(bank_account, transaction, document_types, filters):
vouchers = []
amount_condition = True if "exact_match" in document_types else False
@@ -328,109 +404,90 @@ def get_loan_vouchers(bank_account, transaction, document_types, filters):
return vouchers
+
def get_ld_matching_query(bank_account, amount_condition, filters):
loan_disbursement = frappe.qb.DocType("Loan Disbursement")
matching_reference = loan_disbursement.reference_number == filters.get("reference_number")
- matching_party = loan_disbursement.applicant_type == filters.get("party_type") and \
- loan_disbursement.applicant == filters.get("party")
-
- rank = (
- frappe.qb.terms.Case()
- .when(matching_reference, 1)
- .else_(0)
+ matching_party = loan_disbursement.applicant_type == filters.get(
+ "party_type"
+ ) and loan_disbursement.applicant == filters.get("party")
+
+ rank = frappe.qb.terms.Case().when(matching_reference, 1).else_(0)
+
+ rank1 = frappe.qb.terms.Case().when(matching_party, 1).else_(0)
+
+ query = (
+ frappe.qb.from_(loan_disbursement)
+ .select(
+ rank + rank1 + 1,
+ ConstantColumn("Loan Disbursement").as_("doctype"),
+ loan_disbursement.name,
+ loan_disbursement.disbursed_amount,
+ loan_disbursement.reference_number,
+ loan_disbursement.reference_date,
+ loan_disbursement.applicant_type,
+ loan_disbursement.disbursement_date,
)
-
- rank1 = (
- frappe.qb.terms.Case()
- .when(matching_party, 1)
- .else_(0)
- )
-
- query = frappe.qb.from_(loan_disbursement).select(
- rank + rank1 + 1,
- ConstantColumn("Loan Disbursement").as_("doctype"),
- loan_disbursement.name,
- loan_disbursement.disbursed_amount,
- loan_disbursement.reference_number,
- loan_disbursement.reference_date,
- loan_disbursement.applicant_type,
- loan_disbursement.disbursement_date
- ).where(
- loan_disbursement.docstatus == 1
- ).where(
- loan_disbursement.clearance_date.isnull()
- ).where(
- loan_disbursement.disbursement_account == bank_account
+ .where(loan_disbursement.docstatus == 1)
+ .where(loan_disbursement.clearance_date.isnull())
+ .where(loan_disbursement.disbursement_account == bank_account)
)
if amount_condition:
- query.where(
- loan_disbursement.disbursed_amount == filters.get('amount')
- )
+ query.where(loan_disbursement.disbursed_amount == filters.get("amount"))
else:
- query.where(
- loan_disbursement.disbursed_amount <= filters.get('amount')
- )
+ query.where(loan_disbursement.disbursed_amount <= filters.get("amount"))
vouchers = query.run(as_list=True)
return vouchers
+
def get_lr_matching_query(bank_account, amount_condition, filters):
loan_repayment = frappe.qb.DocType("Loan Repayment")
matching_reference = loan_repayment.reference_number == filters.get("reference_number")
- matching_party = loan_repayment.applicant_type == filters.get("party_type") and \
- loan_repayment.applicant == filters.get("party")
-
- rank = (
- frappe.qb.terms.Case()
- .when(matching_reference, 1)
- .else_(0)
- )
-
- rank1 = (
- frappe.qb.terms.Case()
- .when(matching_party, 1)
- .else_(0)
+ matching_party = loan_repayment.applicant_type == filters.get(
+ "party_type"
+ ) and loan_repayment.applicant == filters.get("party")
+
+ rank = frappe.qb.terms.Case().when(matching_reference, 1).else_(0)
+
+ rank1 = frappe.qb.terms.Case().when(matching_party, 1).else_(0)
+
+ query = (
+ frappe.qb.from_(loan_repayment)
+ .select(
+ rank + rank1 + 1,
+ ConstantColumn("Loan Repayment").as_("doctype"),
+ loan_repayment.name,
+ loan_repayment.amount_paid,
+ loan_repayment.reference_number,
+ loan_repayment.reference_date,
+ loan_repayment.applicant_type,
+ loan_repayment.posting_date,
)
-
- query = frappe.qb.from_(loan_repayment).select(
- rank + rank1 + 1,
- ConstantColumn("Loan Repayment").as_("doctype"),
- loan_repayment.name,
- loan_repayment.amount_paid,
- loan_repayment.reference_number,
- loan_repayment.reference_date,
- loan_repayment.applicant_type,
- loan_repayment.posting_date
- ).where(
- loan_repayment.docstatus == 1
- ).where(
- loan_repayment.clearance_date.isnull()
- ).where(
- loan_repayment.payment_account == bank_account
+ .where(loan_repayment.docstatus == 1)
+ .where(loan_repayment.clearance_date.isnull())
+ .where(loan_repayment.payment_account == bank_account)
)
if amount_condition:
- query.where(
- loan_repayment.amount_paid == filters.get('amount')
- )
+ query.where(loan_repayment.amount_paid == filters.get("amount"))
else:
- query.where(
- loan_repayment.amount_paid <= filters.get('amount')
- )
+ query.where(loan_repayment.amount_paid <= filters.get("amount"))
vouchers = query.run()
return vouchers
+
def get_pe_matching_query(amount_condition, account_from_to, transaction):
# get matching payment entries query
if transaction.deposit > 0:
currency_field = "paid_to_account_currency as currency"
else:
currency_field = "paid_from_account_currency as currency"
- return f"""
+ return f"""
SELECT
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
@@ -519,6 +576,7 @@ def get_si_matching_query(amount_condition):
AND si.docstatus = 1
"""
+
def get_pi_matching_query(amount_condition):
# get matching purchase invoice query
return f"""
@@ -544,11 +602,16 @@ def get_pi_matching_query(amount_condition):
AND cash_bank_account = %(bank_account)s
"""
+
def get_ec_matching_query(bank_account, company, amount_condition):
# get matching Expense Claim query
- mode_of_payments = [x["parent"] for x in frappe.db.get_all("Mode of Payment Account",
- filters={"default_account": bank_account}, fields=["parent"])]
- mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
+ mode_of_payments = [
+ x["parent"]
+ for x in frappe.db.get_all(
+ "Mode of Payment Account", filters={"default_account": bank_account}, fields=["parent"]
+ )
+ ]
+ mode_of_payments = "('" + "', '".join(mode_of_payments) + "' )"
company_currency = get_company_currency(company)
return f"""
SELECT
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 1403303f53c6..3540f0b0e019 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -18,6 +18,7 @@
INVALID_VALUES = ("", None)
+
class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs):
super(BankStatementImport, self).__init__(*args, **kwargs)
@@ -49,16 +50,14 @@ def start_import(self):
self.import_file, self.google_sheets_url
)
- if 'Bank Account' not in json.dumps(preview['columns']):
+ if "Bank Account" not in json.dumps(preview["columns"]):
frappe.throw(_("Please add the Bank Account column"))
from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.utils.scheduler import is_scheduler_inactive
if is_scheduler_inactive() and not frappe.flags.in_test:
- frappe.throw(
- _("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")
- )
+ frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
enqueued_jobs = [d.get("job_name") for d in get_info()]
@@ -81,21 +80,25 @@ def start_import(self):
return False
+
@frappe.whitelist()
def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
import_file, google_sheets_url
)
+
@frappe.whitelist()
def form_start_import(data_import):
return frappe.get_doc("Bank Statement Import", data_import).start_import()
+
@frappe.whitelist()
def download_errored_template(data_import_name):
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows()
+
def parse_data_from_template(raw_data):
data = []
@@ -108,7 +111,10 @@ 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)
@@ -116,7 +122,7 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
data_import = frappe.get_doc("Bank Statement Import", data_import)
file = import_file_path if import_file_path else google_sheets_url
- import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
+ import_file = ImportFile("Bank Transaction", file=file, import_type="Insert New Records")
data = parse_data_from_template(import_file.raw_data)
@@ -136,16 +142,18 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name})
+
def update_mapping_db(bank, template_options):
bank = frappe.get_doc("Bank", bank)
for d in bank.bank_transaction_mapping:
d.delete()
for d in json.loads(template_options)["column_to_field_map"].items():
- bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1] ,"file_field": d[0]} )
+ bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1], "file_field": d[0]})
bank.save()
+
def add_bank_account(data, bank_account):
bank_account_loc = None
if "Bank Account" not in data[0]:
@@ -161,6 +169,7 @@ def add_bank_account(data, bank_account):
else:
row.append(bank_account)
+
def write_files(import_file, data):
full_file_path = import_file.file_doc.get_full_path()
parts = import_file.file_doc.get_extension()
@@ -168,11 +177,12 @@ def write_files(import_file, data):
extension = extension.lstrip(".")
if extension == "csv":
- with open(full_file_path, 'w', newline='') as file:
+ with open(full_file_path, "w", newline="") as file:
writer = csv.writer(file)
writer.writerows(data)
elif extension == "xlsx" or "xls":
- write_xlsx(data, "trans", file_path = full_file_path)
+ write_xlsx(data, "trans", file_path=full_file_path)
+
def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
# from xlsx utils with changes
@@ -187,19 +197,19 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
ws.column_dimensions[get_column_letter(i + 1)].width = column_width
row1 = ws.row_dimensions[1]
- row1.font = Font(name='Calibri', bold=True)
+ row1.font = Font(name="Calibri", bold=True)
for row in data:
clean_row = []
for item in row:
- if isinstance(item, str) and (sheet_name not in ['Data Import Template', 'Data Export']):
+ if isinstance(item, str) and (sheet_name not in ["Data Import Template", "Data Export"]):
value = handle_html(item)
else:
value = item
if isinstance(item, str) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
# Remove illegal characters from the string
- value = re.sub(ILLEGAL_CHARACTERS_RE, '', value)
+ value = re.sub(ILLEGAL_CHARACTERS_RE, "", value)
clean_row.append(value)
@@ -208,19 +218,20 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
wb.save(file_path)
return True
+
@frappe.whitelist()
def upload_bank_statement(**args):
args = frappe._dict(args)
bsi = frappe.new_doc("Bank Statement Import")
if args.company:
- bsi.update({
- "company": args.company,
- })
+ bsi.update(
+ {
+ "company": args.company,
+ }
+ )
if args.bank_account:
- bsi.update({
- "bank_account": args.bank_account
- })
+ bsi.update({"bank_account": args.bank_account})
return bsi
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
index 88aa7ef8b598..2bdaa1049b75 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
@@ -134,7 +134,8 @@
{
"fieldname": "allocated_amount",
"fieldtype": "Currency",
- "label": "Allocated Amount"
+ "label": "Allocated Amount",
+ "options": "currency"
},
{
"fieldname": "amended_from",
@@ -152,7 +153,8 @@
{
"fieldname": "unallocated_amount",
"fieldtype": "Currency",
- "label": "Unallocated Amount"
+ "label": "Unallocated Amount",
+ "options": "currency"
},
{
"fieldname": "party_section",
@@ -192,10 +194,11 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2021-04-14 17:31:58.963529",
+ "modified": "2022-03-21 19:05:04.208222",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -242,6 +245,7 @@
],
"sort_field": "date",
"sort_order": "DESC",
+ "states": [],
"title_field": "bank_account",
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index a476cab55f74..9a0891f14795 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -29,17 +29,26 @@ def on_cancel(self):
def update_allocations(self):
if self.payment_entries:
- allocated_amount = reduce(lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries])
+ allocated_amount = reduce(
+ lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries]
+ )
else:
allocated_amount = 0
if allocated_amount:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
- frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount))
+ frappe.db.set_value(
+ self.doctype,
+ self.name,
+ "unallocated_amount",
+ abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount),
+ )
else:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
- frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)))
+ frappe.db.set_value(
+ self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit))
+ )
amount = self.deposit or self.withdrawal
if amount == self.allocated_amount:
@@ -49,8 +58,14 @@ def update_allocations(self):
def clear_linked_payment_entries(self, for_cancel=False):
for payment_entry in self.payment_entries:
- if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim", "Loan Repayment",
- "Loan Disbursement"]:
+ if payment_entry.payment_document in [
+ "Payment Entry",
+ "Journal Entry",
+ "Purchase Invoice",
+ "Expense Claim",
+ "Loan Repayment",
+ "Loan Disbursement",
+ ]:
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
elif payment_entry.payment_document == "Sales Invoice":
@@ -58,38 +73,41 @@ def clear_linked_payment_entries(self, for_cancel=False):
def clear_simple_entry(self, payment_entry, for_cancel=False):
if payment_entry.payment_document == "Payment Entry":
- if frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type") == "Internal Transfer":
+ if (
+ frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type")
+ == "Internal Transfer"
+ ):
if len(get_reconciled_bank_transactions(payment_entry)) < 2:
return
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
- payment_entry.payment_document, payment_entry.payment_entry,
- "clearance_date", clearance_date)
+ payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", clearance_date
+ )
def clear_sales_invoice(self, payment_entry, for_cancel=False):
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
"Sales Invoice Payment",
- dict(
- parenttype=payment_entry.payment_document,
- parent=payment_entry.payment_entry
- ),
- "clearance_date", clearance_date)
+ dict(parenttype=payment_entry.payment_document, parent=payment_entry.payment_entry),
+ "clearance_date",
+ clearance_date,
+ )
+
def get_reconciled_bank_transactions(payment_entry):
reconciled_bank_transactions = frappe.get_all(
- 'Bank Transaction Payments',
- filters = {
- 'payment_entry': payment_entry.payment_entry
- },
- fields = ['parent']
+ "Bank Transaction Payments",
+ filters={"payment_entry": payment_entry.payment_entry},
+ fields=["parent"],
)
return reconciled_bank_transactions
+
def get_total_allocated_amount(payment_entry):
- return frappe.db.sql("""
+ return frappe.db.sql(
+ """
SELECT
SUM(btp.allocated_amount) as allocated_amount,
bt.name
@@ -102,43 +120,73 @@ def get_total_allocated_amount(payment_entry):
AND
btp.payment_entry = %s
AND
- bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
+ bt.docstatus = 1""",
+ (payment_entry.payment_document, payment_entry.payment_entry),
+ as_dict=True,
+ )
+
def get_paid_amount(payment_entry, currency, 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':
+ if payment_entry.payment_document == "Payment Entry":
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
- paid_amount_field = ("base_paid_amount"
- if doc.paid_to_account_currency == currency else "paid_amount")
- return frappe.db.get_value(payment_entry.payment_document,
- payment_entry.payment_entry, paid_amount_field)
+ if doc.payment_type == "Receive":
+ paid_amount_field = (
+ "received_amount" if doc.paid_to_account_currency == currency else "base_received_amount"
+ )
+ elif doc.payment_type == "Pay":
+ paid_amount_field = (
+ "paid_amount" if doc.paid_to_account_currency == currency else "base_paid_amount"
+ )
+
+ return frappe.db.get_value(
+ payment_entry.payment_document, payment_entry.payment_entry, paid_amount_field
+ )
elif payment_entry.payment_document == "Journal Entry":
- return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account},
- "sum(credit_in_account_currency)")
+ return frappe.db.get_value(
+ "Journal Entry Account",
+ {"parent": payment_entry.payment_entry, "account": bank_account},
+ "sum(credit_in_account_currency)",
+ )
elif payment_entry.payment_document == "Expense Claim":
- return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")
+ return frappe.db.get_value(
+ payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed"
+ )
elif payment_entry.payment_document == "Loan Disbursement":
- return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "disbursed_amount")
+ return frappe.db.get_value(
+ payment_entry.payment_document, payment_entry.payment_entry, "disbursed_amount"
+ )
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"
+ )
else:
- frappe.throw("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry))
+ frappe.throw(
+ "Please reconcile {0}: {1} manually".format(
+ payment_entry.payment_document, payment_entry.payment_entry
+ )
+ )
+
@frappe.whitelist()
def unclear_reference_payment(doctype, docname):
if frappe.db.exists(doctype, docname):
doc = frappe.get_doc(doctype, docname)
if doctype == "Sales Invoice":
- frappe.db.set_value("Sales Invoice Payment", dict(parenttype=doc.payment_document,
- parent=doc.payment_entry), "clearance_date", None)
+ frappe.db.set_value(
+ "Sales Invoice Payment",
+ dict(parenttype=doc.payment_document, parent=doc.payment_entry),
+ "clearance_date",
+ None,
+ )
else:
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
index cca8a88c3016..9f2731de5545 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
@@ -18,12 +18,14 @@ 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("csv".encode("utf-8")):
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("xlsx".encode("utf-8")):
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
+
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
columns = rows[0]
@@ -43,12 +45,10 @@ def create_bank_entries(columns, data, bank_account):
continue
fields = {}
for key, value in header_map.items():
- fields.update({key: d[int(value)-1]})
+ fields.update({key: d[int(value) - 1]})
try:
- bank_transaction = frappe.get_doc({
- "doctype": "Bank Transaction"
- })
+ bank_transaction = frappe.get_doc({"doctype": "Bank Transaction"})
bank_transaction.update(fields)
bank_transaction.date = getdate(parse_date(bank_transaction.date))
bank_transaction.bank_account = bank_account
@@ -61,6 +61,7 @@ def create_bank_entries(columns, data, bank_account):
return {"success": success, "errors": errors}
+
def get_header_mapping(columns, bank_account):
mapping = get_bank_mapping(bank_account)
@@ -71,10 +72,11 @@ def get_header_mapping(columns, bank_account):
return header_map
+
def get_bank_mapping(bank_account):
bank_name = frappe.db.get_value("Bank Account", bank_account, "bank")
bank = frappe.get_doc("Bank", bank_name)
- mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
+ mapping = {row.file_field: row.bank_transaction_field for row in bank.bank_transaction_mapping}
return mapping
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index d84b8e07d354..8cbed4c795d6 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -17,6 +17,7 @@
test_dependencies = ["Item", "Cost Center"]
+
class TestBankTransaction(unittest.TestCase):
@classmethod
def setUpClass(cls):
@@ -41,21 +42,34 @@ def tearDownClass(cls):
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
def test_linked_payments(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
- linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+ bank_transaction = frappe.get_doc(
+ "Bank Transaction",
+ dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
+ )
+ linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
def test_reconcile(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
+ bank_transaction = frappe.get_doc(
+ "Bank Transaction",
+ dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"),
+ )
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
- vouchers = json.dumps([{
- "payment_doctype":"Payment Entry",
- "payment_name":payment.name,
- "amount":bank_transaction.unallocated_amount}])
+ vouchers = json.dumps(
+ [
+ {
+ "payment_doctype": "Payment Entry",
+ "payment_name": payment.name,
+ "amount": bank_transaction.unallocated_amount,
+ }
+ ]
+ )
reconcile_vouchers(bank_transaction.name, vouchers)
- unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
+ unallocated_amount = frappe.db.get_value(
+ "Bank Transaction", bank_transaction.name, "unallocated_amount"
+ )
self.assertTrue(unallocated_amount == 0)
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
@@ -69,122 +83,177 @@ def test_reconcile(self):
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
def test_debit_credit_output(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
- linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+ bank_transaction = frappe.get_doc(
+ "Bank Transaction",
+ dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
+ )
+ linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
self.assertTrue(linked_payments[0][3])
# Check error if already reconciled
def test_already_reconciled(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
+ bank_transaction = frappe.get_doc(
+ "Bank Transaction",
+ dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"),
+ )
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
- vouchers = json.dumps([{
- "payment_doctype":"Payment Entry",
- "payment_name":payment.name,
- "amount":bank_transaction.unallocated_amount}])
+ vouchers = json.dumps(
+ [
+ {
+ "payment_doctype": "Payment Entry",
+ "payment_name": payment.name,
+ "amount": bank_transaction.unallocated_amount,
+ }
+ ]
+ )
reconcile_vouchers(bank_transaction.name, vouchers)
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
+ bank_transaction = frappe.get_doc(
+ "Bank Transaction",
+ dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"),
+ )
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
- vouchers = json.dumps([{
- "payment_doctype":"Payment Entry",
- "payment_name":payment.name,
- "amount":bank_transaction.unallocated_amount}])
- self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers)
+ vouchers = json.dumps(
+ [
+ {
+ "payment_doctype": "Payment Entry",
+ "payment_name": payment.name,
+ "amount": bank_transaction.unallocated_amount,
+ }
+ ]
+ )
+ self.assertRaises(
+ frappe.ValidationError,
+ reconcile_vouchers,
+ bank_transaction_name=bank_transaction.name,
+ vouchers=vouchers,
+ )
# Raise an error if debitor transaction vs debitor payment
def test_clear_sales_invoice(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
+ bank_transaction = frappe.get_doc(
+ "Bank Transaction",
+ dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"),
+ )
payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"]))
- vouchers = json.dumps([{
- "payment_doctype":"Sales Invoice",
- "payment_name":payment.name,
- "amount":bank_transaction.unallocated_amount}])
+ vouchers = json.dumps(
+ [
+ {
+ "payment_doctype": "Sales Invoice",
+ "payment_name": payment.name,
+ "amount": bank_transaction.unallocated_amount,
+ }
+ ]
+ )
reconcile_vouchers(bank_transaction.name, vouchers=vouchers)
- self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
- self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
+ self.assertEqual(
+ frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0
+ )
+ self.assertTrue(
+ frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date")
+ is not None
+ )
+
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
try:
- frappe.get_doc({
- "doctype": "Bank",
- "bank_name":bank_name,
- }).insert(ignore_if_duplicate=True)
+ frappe.get_doc(
+ {
+ "doctype": "Bank",
+ "bank_name": bank_name,
+ }
+ ).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
try:
- frappe.get_doc({
- "doctype": "Bank Account",
- "account_name":"Checking Account",
- "bank": bank_name,
- "account": account_name
- }).insert(ignore_if_duplicate=True)
+ frappe.get_doc(
+ {
+ "doctype": "Bank Account",
+ "account_name": "Checking Account",
+ "bank": bank_name,
+ "account": account_name,
+ }
+ ).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
+
def add_transactions():
create_bank_account()
- doc = frappe.get_doc({
- "doctype": "Bank Transaction",
- "description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
- "date": "2018-10-23",
- "deposit": 1200,
- "currency": "INR",
- "bank_account": "Checking Account - Citi Bank"
- }).insert()
+ doc = frappe.get_doc(
+ {
+ "doctype": "Bank Transaction",
+ "description": "1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
+ "date": "2018-10-23",
+ "deposit": 1200,
+ "currency": "INR",
+ "bank_account": "Checking Account - Citi Bank",
+ }
+ ).insert()
doc.submit()
- doc = frappe.get_doc({
- "doctype": "Bank Transaction",
- "description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
- "date": "2018-10-23",
- "deposit": 1700,
- "currency": "INR",
- "bank_account": "Checking Account - Citi Bank"
- }).insert()
+ doc = frappe.get_doc(
+ {
+ "doctype": "Bank Transaction",
+ "description": "1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
+ "date": "2018-10-23",
+ "deposit": 1700,
+ "currency": "INR",
+ "bank_account": "Checking Account - Citi Bank",
+ }
+ ).insert()
doc.submit()
- doc = frappe.get_doc({
- "doctype": "Bank Transaction",
- "description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
- "date": "2018-10-26",
- "withdrawal": 690,
- "currency": "INR",
- "bank_account": "Checking Account - Citi Bank"
- }).insert()
+ doc = frappe.get_doc(
+ {
+ "doctype": "Bank Transaction",
+ "description": "Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
+ "date": "2018-10-26",
+ "withdrawal": 690,
+ "currency": "INR",
+ "bank_account": "Checking Account - Citi Bank",
+ }
+ ).insert()
doc.submit()
- doc = frappe.get_doc({
- "doctype": "Bank Transaction",
- "description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
- "date": "2018-10-27",
- "deposit": 3900,
- "currency": "INR",
- "bank_account": "Checking Account - Citi Bank"
- }).insert()
+ doc = frappe.get_doc(
+ {
+ "doctype": "Bank Transaction",
+ "description": "Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
+ "date": "2018-10-27",
+ "deposit": 3900,
+ "currency": "INR",
+ "bank_account": "Checking Account - Citi Bank",
+ }
+ ).insert()
doc.submit()
- doc = frappe.get_doc({
- "doctype": "Bank Transaction",
- "description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
- "date": "2018-10-27",
- "withdrawal": 109080,
- "currency": "INR",
- "bank_account": "Checking Account - Citi Bank"
- }).insert()
+ doc = frappe.get_doc(
+ {
+ "doctype": "Bank Transaction",
+ "description": "I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
+ "date": "2018-10-27",
+ "withdrawal": 109080,
+ "currency": "INR",
+ "bank_account": "Checking Account - Citi Bank",
+ }
+ ).insert()
doc.submit()
def add_vouchers():
try:
- frappe.get_doc({
- "doctype": "Supplier",
- "supplier_group":"All Supplier Groups",
- "supplier_type": "Company",
- "supplier_name": "Conrad Electronic"
- }).insert(ignore_if_duplicate=True)
+ frappe.get_doc(
+ {
+ "doctype": "Supplier",
+ "supplier_group": "All Supplier Groups",
+ "supplier_type": "Company",
+ "supplier_name": "Conrad Electronic",
+ }
+ ).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
@@ -198,12 +267,14 @@ def add_vouchers():
pe.submit()
try:
- frappe.get_doc({
- "doctype": "Supplier",
- "supplier_group":"All Supplier Groups",
- "supplier_type": "Company",
- "supplier_name": "Mr G"
- }).insert(ignore_if_duplicate=True)
+ frappe.get_doc(
+ {
+ "doctype": "Supplier",
+ "supplier_group": "All Supplier Groups",
+ "supplier_type": "Company",
+ "supplier_name": "Mr G",
+ }
+ ).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
@@ -222,26 +293,30 @@ def add_vouchers():
pe.submit()
try:
- frappe.get_doc({
- "doctype": "Supplier",
- "supplier_group":"All Supplier Groups",
- "supplier_type": "Company",
- "supplier_name": "Poore Simon's"
- }).insert(ignore_if_duplicate=True)
+ frappe.get_doc(
+ {
+ "doctype": "Supplier",
+ "supplier_group": "All Supplier Groups",
+ "supplier_type": "Company",
+ "supplier_name": "Poore Simon's",
+ }
+ ).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
try:
- frappe.get_doc({
- "doctype": "Customer",
- "customer_group":"All Customer Groups",
- "customer_type": "Company",
- "customer_name": "Poore Simon's"
- }).insert(ignore_if_duplicate=True)
+ frappe.get_doc(
+ {
+ "doctype": "Customer",
+ "customer_group": "All Customer Groups",
+ "customer_type": "Company",
+ "customer_name": "Poore Simon's",
+ }
+ ).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
- pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1)
+ pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save=1)
pi.cash_bank_account = "_Test Bank - _TC"
pi.insert()
pi.submit()
@@ -261,33 +336,31 @@ def add_vouchers():
pe.submit()
try:
- frappe.get_doc({
- "doctype": "Customer",
- "customer_group":"All Customer Groups",
- "customer_type": "Company",
- "customer_name": "Fayva"
- }).insert(ignore_if_duplicate=True)
+ frappe.get_doc(
+ {
+ "doctype": "Customer",
+ "customer_group": "All Customer Groups",
+ "customer_type": "Company",
+ "customer_name": "Fayva",
+ }
+ ).insert(ignore_if_duplicate=True)
except frappe.DuplicateEntryError:
pass
- mode_of_payment = frappe.get_doc({
- "doctype": "Mode of Payment",
- "name": "Cash"
- })
+ mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
- if not frappe.db.get_value('Mode of Payment Account', {'company': "_Test Company", 'parent': "Cash"}):
- mode_of_payment.append("accounts", {
- "company": "_Test Company",
- "default_account": "_Test Bank - _TC"
- })
+ if not frappe.db.get_value(
+ "Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
+ ):
+ mode_of_payment.append(
+ "accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
+ )
mode_of_payment.save()
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
si.is_pos = 1
- si.append("payments", {
- "mode_of_payment": "Cash",
- "account": "_Test Bank - _TC",
- "amount": 109080
- })
+ si.append(
+ "payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080}
+ )
si.insert()
si.submit()
diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py
index 492bb3655895..5527f9fb99fb 100644
--- a/erpnext/accounts/doctype/budget/budget.py
+++ b/erpnext/accounts/doctype/budget/budget.py
@@ -14,13 +14,19 @@
from erpnext.accounts.utils import get_fiscal_year
-class BudgetError(frappe.ValidationError): pass
-class DuplicateBudgetError(frappe.ValidationError): pass
+class BudgetError(frappe.ValidationError):
+ pass
+
+
+class DuplicateBudgetError(frappe.ValidationError):
+ pass
+
class Budget(Document):
def autoname(self):
- self.name = make_autoname(self.get(frappe.scrub(self.budget_against))
- + "/" + self.fiscal_year + "/.###")
+ self.name = make_autoname(
+ self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###"
+ )
def validate(self):
if not self.get(frappe.scrub(self.budget_against)):
@@ -35,34 +41,44 @@ def validate_duplicate(self):
budget_against = self.get(budget_against_field)
accounts = [d.account for d in self.accounts] or []
- existing_budget = frappe.db.sql("""
+ existing_budget = frappe.db.sql(
+ """
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), as_dict=1)
+ % ("%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,
+ )
for d in existing_budget:
- frappe.throw(_("Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4}")
- .format(d.name, self.budget_against, budget_against, d.account, self.fiscal_year), DuplicateBudgetError)
+ frappe.throw(
+ _(
+ "Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4}"
+ ).format(d.name, self.budget_against, budget_against, d.account, self.fiscal_year),
+ DuplicateBudgetError,
+ )
def validate_accounts(self):
account_list = []
- for d in self.get('accounts'):
+ for d in self.get("accounts"):
if d.account:
- account_details = frappe.db.get_value("Account", d.account,
- ["is_group", "company", "report_type"], as_dict=1)
+ account_details = frappe.db.get_value(
+ "Account", d.account, ["is_group", "company", "report_type"], as_dict=1
+ )
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))
+ frappe.throw(
+ _("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format(
+ d.account
+ )
+ )
if d.account in account_list:
frappe.throw(_("Account {0} has been entered multiple times").format(d.account))
@@ -70,51 +86,66 @@ def validate_accounts(self):
account_list.append(d.account)
def set_null_value(self):
- if self.budget_against == 'Cost Center':
+ if self.budget_against == "Cost Center":
self.project = None
else:
self.cost_center = None
def validate_applicable_for(self):
- if (self.applicable_on_material_request
- and not (self.applicable_on_purchase_order and self.applicable_on_booking_actual_expenses)):
- frappe.throw(_("Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses"))
-
- elif (self.applicable_on_purchase_order
- and not (self.applicable_on_booking_actual_expenses)):
+ if self.applicable_on_material_request and not (
+ self.applicable_on_purchase_order and self.applicable_on_booking_actual_expenses
+ ):
+ frappe.throw(
+ _("Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses")
+ )
+
+ elif self.applicable_on_purchase_order and not (self.applicable_on_booking_actual_expenses):
frappe.throw(_("Please enable Applicable on Booking Actual Expenses"))
- elif not(self.applicable_on_material_request
- or self.applicable_on_purchase_order or self.applicable_on_booking_actual_expenses):
+ elif not (
+ self.applicable_on_material_request
+ or self.applicable_on_purchase_order
+ or self.applicable_on_booking_actual_expenses
+ ):
self.applicable_on_booking_actual_expenses = 1
+
def validate_expense_against_budget(args):
args = frappe._dict(args)
- if args.get('company') and not args.fiscal_year:
- args.fiscal_year = get_fiscal_year(args.get('posting_date'), company=args.get('company'))[0]
- frappe.flags.exception_approver_role = frappe.get_cached_value('Company',
- args.get('company'), 'exception_budget_approver_role')
+ if args.get("company") and not args.fiscal_year:
+ args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
+ frappe.flags.exception_approver_role = frappe.get_cached_value(
+ "Company", args.get("company"), "exception_budget_approver_role"
+ )
if not args.account:
args.account = args.get("expense_account")
- if not (args.get('account') and args.get('cost_center')) and args.item_code:
+ if not (args.get("account") and args.get("cost_center")) and args.item_code:
args.cost_center, args.account = get_item_details(args)
if not args.account:
return
- for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
- if (args.get(budget_against) and args.account
- and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
+ for budget_against in ["project", "cost_center"] + get_accounting_dimensions():
+ if (
+ args.get(budget_against)
+ and args.account
+ and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
+ ):
doctype = frappe.unscrub(budget_against)
- if frappe.get_cached_value('DocType', doctype, 'is_tree'):
+ if frappe.get_cached_value("DocType", doctype, "is_tree"):
lft, rgt = frappe.db.get_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
+ where lft<=%s and rgt>=%s and name=b.%s)""" % (
+ doctype,
+ lft,
+ rgt,
+ budget_against,
+ ) # nosec
args.is_tree = True
else:
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
@@ -123,7 +154,8 @@ def validate_expense_against_budget(args):
args.budget_against_field = budget_against
args.budget_against_doctype = doctype
- budget_records = frappe.db.sql("""
+ budget_records = frappe.db.sql(
+ """
select
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
ifnull(b.applicable_on_material_request, 0) as for_material_request,
@@ -138,11 +170,17 @@ def validate_expense_against_budget(args):
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
+ """.format(
+ condition=condition, budget_against_field=budget_against
+ ),
+ (args.fiscal_year, args.account),
+ as_dict=True,
+ ) # nosec
if budget_records:
validate_budget_records(args, budget_records)
+
def validate_budget_records(args, budget_records):
for budget in budget_records:
if flt(budget.budget_amount):
@@ -150,88 +188,118 @@ def validate_budget_records(args, budget_records):
yearly_action, monthly_action = get_actions(args, budget)
if monthly_action in ["Stop", "Warn"]:
- budget_amount = get_accumulated_monthly_budget(budget.monthly_distribution,
- args.posting_date, args.fiscal_year, budget.budget_amount)
+ budget_amount = get_accumulated_monthly_budget(
+ budget.monthly_distribution, args.posting_date, args.fiscal_year, budget.budget_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)
+ compare_expense_with_budget(
+ args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
+ )
+
+ if (
+ yearly_action in ("Stop", "Warn")
+ and monthly_action != "Stop"
+ and yearly_action != monthly_action
+ ):
+ compare_expense_with_budget(
+ args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
+ )
- if yearly_action in ("Stop", "Warn") and monthly_action != "Stop" \
- and yearly_action != monthly_action:
- compare_expense_with_budget(args, flt(budget.budget_amount),
- _("Annual"), yearly_action, budget.budget_against, amount)
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
actual_expense = amount or get_actual_expense(args)
if actual_expense > budget_amount:
diff = actual_expense - budget_amount
- currency = frappe.get_cached_value('Company', args.company, 'default_currency')
+ currency = frappe.get_cached_value("Company", args.company, "default_currency")
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
- _(action_for), frappe.bold(args.account), args.budget_against_field,
- frappe.bold(budget_against),
- frappe.bold(fmt_money(budget_amount, currency=currency)),
- 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)):
+ _(action_for),
+ frappe.bold(args.account),
+ args.budget_against_field,
+ frappe.bold(budget_against),
+ frappe.bold(fmt_money(budget_amount, currency=currency)),
+ 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)
+ ):
action = "Warn"
- if action=="Stop":
+ if action == "Stop":
frappe.throw(msg, BudgetError)
else:
- frappe.msgprint(msg, indicator='orange')
+ frappe.msgprint(msg, indicator="orange")
+
def get_actions(args, budget):
yearly_action = budget.action_if_annual_budget_exceeded
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
- if args.get('doctype') == 'Material Request' and budget.for_material_request:
+ if args.get("doctype") == "Material Request" and budget.for_material_request:
yearly_action = budget.action_if_annual_budget_exceeded_on_mr
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded_on_mr
- elif args.get('doctype') == 'Purchase Order' and budget.for_purchase_order:
+ elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
yearly_action = budget.action_if_annual_budget_exceeded_on_po
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded_on_po
return yearly_action, monthly_action
+
def get_amount(args, budget):
amount = 0
- if args.get('doctype') == 'Material Request' and budget.for_material_request:
- amount = (get_requested_amount(args, budget)
- + get_ordered_amount(args, budget) + get_actual_expense(args))
+ if args.get("doctype") == "Material Request" and budget.for_material_request:
+ amount = (
+ get_requested_amount(args, budget) + get_ordered_amount(args, budget) + get_actual_expense(args)
+ )
- elif args.get('doctype') == 'Purchase Order' and budget.for_purchase_order:
+ elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
amount = get_ordered_amount(args, budget) + get_actual_expense(args)
return amount
+
def get_requested_amount(args, budget):
- item_code = args.get('item_code')
- condition = get_other_condition(args, budget, 'Material Request')
+ item_code = args.get("item_code")
+ condition = get_other_condition(args, budget, "Material Request")
- data = frappe.db.sql(""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
+ 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), item_code, as_list=1)
+ parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(
+ condition
+ ),
+ item_code,
+ as_list=1,
+ )
return data[0][0] if data else 0
+
def get_ordered_amount(args, budget):
- item_code = args.get('item_code')
- condition = get_other_condition(args, budget, 'Purchase Order')
+ item_code = args.get("item_code")
+ condition = get_other_condition(args, budget, "Purchase Order")
- data = frappe.db.sql(""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
+ data = frappe.db.sql(
+ """ 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), item_code, as_list=1)
+ and parent.status != 'Closed' and {0}""".format(
+ condition
+ ),
+ item_code,
+ as_list=1,
+ )
return data[0][0] if data else 0
+
def get_other_condition(args, budget, for_doc):
condition = "expense_account = '%s'" % (args.expense_account)
budget_against_field = args.get("budget_against_field")
@@ -239,41 +307,51 @@ def get_other_condition(args, budget, for_doc):
if budget_against_field and args.get(budget_against_field):
condition += " and child.%s = '%s'" % (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'
- start_date, end_date = frappe.db.get_value('Fiscal Year', args.get('fiscal_year'),
- ['year_start_date', 'year_end_date'])
+ if args.get("fiscal_year"):
+ date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
+ start_date, end_date = frappe.db.get_value(
+ "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)
+ between '%s' and '%s' """ % (
+ date_field,
+ start_date,
+ end_date,
+ )
return condition
+
def get_actual_expense(args):
if not args.budget_against_doctype:
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
- budget_against_field = args.get('budget_against_field')
- condition1 = " and gle.posting_date <= %(month_end_date)s" \
- if args.get("month_end_date") else ""
+ budget_against_field = args.get("budget_against_field")
+ condition1 = " and gle.posting_date <= %(month_end_date)s" if args.get("month_end_date") else ""
if args.is_tree:
- lft_rgt = frappe.db.get_value(args.budget_against_doctype,
- args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
+ lft_rgt = frappe.db.get_value(
+ args.budget_against_doctype, args.get(budget_against_field), ["lft", "rgt"], as_dict=1
+ )
args.update(lft_rgt)
condition2 = """and exists(select name from `tab{doctype}`
where lft>=%(lft)s and rgt<=%(rgt)s
- and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
- budget_against_field=budget_against_field)
+ and name=gle.{budget_against_field})""".format(
+ doctype=args.budget_against_doctype, budget_against_field=budget_against_field # nosec
+ )
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)
+ gle.{budget_against} = %({budget_against})s)""".format(
+ doctype=args.budget_against_doctype, budget_against=budget_against_field
+ )
- amount = flt(frappe.db.sql("""
+ amount = flt(
+ frappe.db.sql(
+ """
select sum(gle.debit) - sum(gle.credit)
from `tabGL Entry` gle
where gle.account=%(account)s
@@ -282,46 +360,59 @@ def get_actual_expense(args):
and gle.company=%(company)s
and gle.docstatus=1
{condition2}
- """.format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
+ """.format(
+ condition1=condition1, condition2=condition2
+ ),
+ (args),
+ )[0][0]
+ ) # nosec
return amount
+
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
distribution = {}
if monthly_distribution:
- for d in frappe.db.sql("""select mdp.month, mdp.percentage_allocation
+ for d in frappe.db.sql(
+ """select mdp.month, mdp.percentage_allocation
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
- where mdp.parent=md.name and md.fiscal_year=%s""", fiscal_year, as_dict=1):
- distribution.setdefault(d.month, d.percentage_allocation)
+ where mdp.parent=md.name and md.fiscal_year=%s""",
+ fiscal_year,
+ as_dict=1,
+ ):
+ distribution.setdefault(d.month, d.percentage_allocation)
dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")
accumulated_percentage = 0.0
- while(dt <= getdate(posting_date)):
+ while dt <= getdate(posting_date):
if monthly_distribution:
accumulated_percentage += distribution.get(getdate(dt).strftime("%B"), 0)
else:
- accumulated_percentage += 100.0/12
+ accumulated_percentage += 100.0 / 12
dt = add_months(dt, 1)
return annual_budget * accumulated_percentage / 100
+
def get_item_details(args):
cost_center, expense_account = None, None
- if not args.get('company'):
+ if not args.get("company"):
return cost_center, expense_account
if args.item_code:
- item_defaults = frappe.db.get_value('Item Default',
- {'parent': args.item_code, 'company': args.get('company')},
- ['buying_cost_center', 'expense_account'])
+ item_defaults = frappe.db.get_value(
+ "Item Default",
+ {"parent": args.item_code, "company": args.get("company")},
+ ["buying_cost_center", "expense_account"],
+ )
if item_defaults:
cost_center, expense_account = item_defaults
if not (cost_center and expense_account):
- for doctype in ['Item Group', 'Company']:
+ for doctype in ["Item Group", "Company"]:
data = get_expense_cost_center(doctype, args)
if not cost_center and data:
@@ -335,11 +426,15 @@ def get_item_details(args):
return cost_center, expense_account
+
def get_expense_cost_center(doctype, args):
- if doctype == 'Item Group':
- return frappe.db.get_value('Item Default',
- {'parent': args.get(frappe.scrub(doctype)), 'company': args.get('company')},
- ['buying_cost_center', 'expense_account'])
+ if doctype == "Item Group":
+ return frappe.db.get_value(
+ "Item Default",
+ {"parent": args.get(frappe.scrub(doctype)), "company": args.get("company")},
+ ["buying_cost_center", "expense_account"],
+ )
else:
- return frappe.db.get_value(doctype, args.get(frappe.scrub(doctype)),\
- ['cost_center', 'default_expense_account'])
+ return frappe.db.get_value(
+ doctype, args.get(frappe.scrub(doctype)), ["cost_center", "default_expense_account"]
+ )
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index 9a83a0aa9a6d..c48c7d97a2ab 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -11,7 +11,8 @@
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-test_dependencies = ['Monthly Distribution']
+test_dependencies = ["Monthly Distribution"]
+
class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self):
@@ -19,11 +20,18 @@ def test_monthly_budget_crossed_ignore(self):
budget = make_budget(budget_against="Cost Center")
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 40000,
+ "_Test Cost Center - _TC",
+ posting_date=nowdate(),
+ submit=True,
+ )
- self.assertTrue(frappe.db.get_value("GL Entry",
- {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
+ self.assertTrue(
+ frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
+ )
budget.cancel()
jv.cancel()
@@ -33,10 +41,17 @@ def test_monthly_budget_crossed_stop1(self):
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",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 40000,
+ "_Test Cost Center - _TC",
+ posting_date=nowdate(),
+ )
self.assertRaises(BudgetError, jv.submit)
@@ -48,49 +63,65 @@ def test_exception_approver_role(self):
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",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 40000,
+ "_Test Cost Center - _TC",
+ posting_date=nowdate(),
+ )
self.assertRaises(BudgetError, jv.submit)
- frappe.db.set_value('Company', budget.company, 'exception_budget_approver_role', 'Accounts User')
+ frappe.db.set_value("Company", budget.company, "exception_budget_approver_role", "Accounts User")
jv.submit()
- self.assertEqual(frappe.db.get_value('Journal Entry', jv.name, 'docstatus'), 1)
+ self.assertEqual(frappe.db.get_value("Journal Entry", jv.name, "docstatus"), 1)
jv.cancel()
- frappe.db.set_value('Company', budget.company, 'exception_budget_approver_role', '')
+ frappe.db.set_value("Company", budget.company, "exception_budget_approver_role", "")
budget.load_from_db()
budget.cancel()
def test_monthly_budget_crossed_for_mr(self):
- budget = make_budget(applicable_on_material_request=1,
- applicable_on_purchase_order=1, action_if_accumulated_monthly_budget_exceeded_on_mr="Stop",
- budget_against="Cost Center")
+ budget = make_budget(
+ applicable_on_material_request=1,
+ applicable_on_purchase_order=1,
+ action_if_accumulated_monthly_budget_exceeded_on_mr="Stop",
+ budget_against="Cost Center",
+ )
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({
- "doctype": "Material Request",
- "material_request_type": "Purchase",
- "transaction_date": nowdate(),
- "company": budget.company,
- "items": [{
- 'item_code': '_Test Item',
- 'qty': 1,
- 'uom': "_Test UOM",
- 'warehouse': '_Test Warehouse - _TC',
- 'schedule_date': nowdate(),
- 'rate': 100000,
- 'expense_account': '_Test Account Cost for Goods Sold - _TC',
- 'cost_center': '_Test Cost Center - _TC'
- }]
- })
+ mr = frappe.get_doc(
+ {
+ "doctype": "Material Request",
+ "material_request_type": "Purchase",
+ "transaction_date": nowdate(),
+ "company": budget.company,
+ "items": [
+ {
+ "item_code": "_Test Item",
+ "qty": 1,
+ "uom": "_Test UOM",
+ "warehouse": "_Test Warehouse - _TC",
+ "schedule_date": nowdate(),
+ "rate": 100000,
+ "expense_account": "_Test Account Cost for Goods Sold - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ }
+ ],
+ }
+ )
mr.set_missing_values()
@@ -100,11 +131,16 @@ def test_monthly_budget_crossed_for_mr(self):
budget.cancel()
def test_monthly_budget_crossed_for_po(self):
- budget = make_budget(applicable_on_purchase_order=1,
- action_if_accumulated_monthly_budget_exceeded_on_po="Stop", budget_against="Cost Center")
+ budget = make_budget(
+ applicable_on_purchase_order=1,
+ action_if_accumulated_monthly_budget_exceeded_on_po="Stop",
+ budget_against="Cost Center",
+ )
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)
@@ -122,12 +158,20 @@ def test_monthly_budget_crossed_stop2(self):
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"})
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project=project, posting_date=nowdate())
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 40000,
+ "_Test Cost Center - _TC",
+ project=project,
+ posting_date=nowdate(),
+ )
self.assertRaises(BudgetError, jv.submit)
@@ -139,8 +183,13 @@ def test_yearly_budget_crossed_stop1(self):
budget = make_budget(budget_against="Cost Center")
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 250000, "_Test Cost Center - _TC", posting_date=nowdate())
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 250000,
+ "_Test Cost Center - _TC",
+ posting_date=nowdate(),
+ )
self.assertRaises(BudgetError, jv.submit)
@@ -153,9 +202,14 @@ def test_yearly_budget_crossed_stop2(self):
project = frappe.get_value("Project", {"project_name": "_Test Project"})
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 250000, "_Test Cost Center - _TC",
- project=project, posting_date=nowdate())
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 250000,
+ "_Test Cost Center - _TC",
+ project=project,
+ posting_date=nowdate(),
+ )
self.assertRaises(BudgetError, jv.submit)
@@ -169,14 +223,23 @@ def test_monthly_budget_on_cancellation1(self):
if month > 9:
month = 9
- for i in range(month+1):
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
-
- self.assertTrue(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")
+ for i in range(month + 1):
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 20000,
+ "_Test Cost Center - _TC",
+ posting_date=nowdate(),
+ submit=True,
+ )
+
+ self.assertTrue(
+ 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"
+ )
self.assertRaises(BudgetError, jv.cancel)
@@ -193,14 +256,23 @@ def test_monthly_budget_on_cancellation2(self):
project = frappe.get_value("Project", {"project_name": "_Test Project"})
for i in range(month + 1):
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True,
- project=project)
-
- self.assertTrue(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")
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 20000,
+ "_Test Cost Center - _TC",
+ posting_date=nowdate(),
+ submit=True,
+ project=project,
+ )
+
+ self.assertTrue(
+ 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"
+ )
self.assertRaises(BudgetError, jv.cancel)
@@ -212,10 +284,17 @@ def test_monthly_budget_against_group_cost_center(self):
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")
-
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date=nowdate())
+ 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",
+ "_Test Bank - _TC",
+ 40000,
+ "_Test Cost Center 2 - _TC",
+ posting_date=nowdate(),
+ )
self.assertRaises(BudgetError, jv.submit)
@@ -226,19 +305,28 @@ def test_monthly_budget_against_parent_group_cost_center(self):
cost_center = "_Test Cost Center 3 - _TC"
if not frappe.db.exists("Cost Center", cost_center):
- frappe.get_doc({
- 'doctype': 'Cost Center',
- 'cost_center_name': '_Test Cost Center 3',
- 'parent_cost_center': "_Test Company - _TC",
- 'company': '_Test Company',
- 'is_group': 0
- }).insert(ignore_permissions=True)
+ frappe.get_doc(
+ {
+ "doctype": "Cost Center",
+ "cost_center_name": "_Test Cost Center 3",
+ "parent_cost_center": "_Test Company - _TC",
+ "company": "_Test Company",
+ "is_group": 0,
+ }
+ ).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")
-
- jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, cost_center, posting_date=nowdate())
+ 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",
+ "_Test Bank - _TC",
+ 40000,
+ cost_center,
+ posting_date=nowdate(),
+ )
self.assertRaises(BudgetError, jv.submit)
@@ -255,14 +343,16 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
fiscal_year = get_fiscal_year(nowdate())[0]
- args = frappe._dict({
- "account": "_Test Account Cost for Goods Sold - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "monthly_end_date": posting_date,
- "company": "_Test Company",
- "fiscal_year": fiscal_year,
- "budget_against_field": budget_against_field,
- })
+ args = frappe._dict(
+ {
+ "account": "_Test Account Cost for Goods Sold - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "monthly_end_date": posting_date,
+ "company": "_Test Company",
+ "fiscal_year": fiscal_year,
+ "budget_against_field": budget_against_field,
+ }
+ )
if not args.get(budget_against_field):
args[budget_against_field] = budget_against
@@ -271,26 +361,42 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
if existing_expense:
if budget_against_field == "cost_center":
- make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
+ make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ -existing_expense,
+ "_Test Cost Center - _TC",
+ posting_date=nowdate(),
+ submit=True,
+ )
elif budget_against_field == "project":
- make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project=budget_against, posting_date=nowdate())
+ make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ -existing_expense,
+ "_Test Cost Center - _TC",
+ submit=True,
+ project=budget_against,
+ posting_date=nowdate(),
+ )
+
def make_budget(**args):
args = frappe._dict(args)
- budget_against=args.budget_against
- cost_center=args.cost_center
+ budget_against = args.budget_against
+ cost_center = args.cost_center
fiscal_year = get_fiscal_year(nowdate())[0]
if budget_against == "Project":
project_name = "{0}%".format("_Test Project/" + fiscal_year)
- budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", project_name)})
+ 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)})
+ 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)
@@ -300,7 +406,7 @@ def make_budget(**args):
if budget_against == "Project":
budget.project = frappe.get_value("Project", {"project_name": "_Test Project"})
else:
- budget.cost_center =cost_center or "_Test Cost Center - _TC"
+ budget.cost_center = cost_center or "_Test Cost Center - _TC"
monthly_distribution = frappe.get_doc("Monthly Distribution", "_Test Distribution")
monthly_distribution.fiscal_year = fiscal_year
@@ -312,20 +418,27 @@ 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_accumulated_monthly_budget_exceeded_on_mr = args.action_if_accumulated_monthly_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_accumulated_monthly_budget_exceeded_on_po = args.action_if_accumulated_monthly_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"
+ )
budget.insert()
budget.submit()
diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py
index 61331d32d8ee..0de75c786973 100644
--- a/erpnext/accounts/doctype/c_form/c_form.py
+++ b/erpnext/accounts/doctype/c_form/c_form.py
@@ -11,28 +11,42 @@
class CForm(Document):
def validate(self):
"""Validate invoice that c-form is applicable
- and no other c-form is received for that"""
+ and no other c-form is received for that"""
- for d in self.get('invoices'):
+ for d in self.get("invoices"):
if d.invoice_no:
- inv = frappe.db.sql("""select c_form_applicable, c_form_no from
- `tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no)
+ inv = frappe.db.sql(
+ """select c_form_applicable, c_form_no from
+ `tabSales Invoice` where name = %s and docstatus = 1""",
+ d.invoice_no,
+ )
- if inv and inv[0][0] != 'Yes':
+ if inv and inv[0][0] != "Yes":
frappe.throw(_("C-form is not applicable for Invoice: {0}").format(d.invoice_no))
elif inv and inv[0][1] and inv[0][1] != self.name:
- frappe.throw(_("""Invoice {0} is tagged in another C-form: {1}.
+ frappe.throw(
+ _(
+ """Invoice {0} is tagged in another C-form: {1}.
If you want to change C-form no for this invoice,
- please remove invoice no from the previous c-form and then try again"""\
- .format(d.invoice_no, inv[0][1])))
+ please remove invoice no from the previous c-form and then try again""".format(
+ d.invoice_no, inv[0][1]
+ )
+ )
+ )
elif not inv:
- frappe.throw(_("Row {0}: Invoice {1} is invalid, it might be cancelled / does not exist. \
- Please enter a valid Invoice".format(d.idx, d.invoice_no)))
+ frappe.throw(
+ _(
+ "Row {0}: Invoice {1} is invalid, it might be cancelled / does not exist. \
+ Please enter a valid Invoice".format(
+ d.idx, d.invoice_no
+ )
+ )
+ )
def on_update(self):
- """ Update C-Form No on invoices"""
+ """Update C-Form No on invoices"""
self.set_total_invoiced_amount()
def on_submit(self):
@@ -43,30 +57,40 @@ def before_cancel(self):
frappe.db.sql("""update `tabSales Invoice` set c_form_no=null where c_form_no=%s""", self.name)
def set_cform_in_sales_invoices(self):
- inv = [d.invoice_no for d in self.get('invoices')]
+ inv = [d.invoice_no for d in self.get("invoices")]
if inv:
- frappe.db.sql("""update `tabSales Invoice` set c_form_no=%s, modified=%s where name in (%s)""" %
- ('%s', '%s', ', '.join(['%s'] * len(inv))), tuple([self.name, self.modified] + inv))
+ frappe.db.sql(
+ """update `tabSales Invoice` set c_form_no=%s, modified=%s where name in (%s)"""
+ % ("%s", "%s", ", ".join(["%s"] * len(inv))),
+ tuple([self.name, self.modified] + inv),
+ )
- frappe.db.sql("""update `tabSales Invoice` set c_form_no = null, modified = %s
- where name not in (%s) and ifnull(c_form_no, '') = %s""" %
- ('%s', ', '.join(['%s']*len(inv)), '%s'), tuple([self.modified] + inv + [self.name]))
+ frappe.db.sql(
+ """update `tabSales Invoice` set c_form_no = null, modified = %s
+ where name not in (%s) and ifnull(c_form_no, '') = %s"""
+ % ("%s", ", ".join(["%s"] * len(inv)), "%s"),
+ tuple([self.modified] + inv + [self.name]),
+ )
else:
frappe.throw(_("Please enter atleast 1 invoice in the table"))
def set_total_invoiced_amount(self):
- total = sum(flt(d.grand_total) for d in self.get('invoices'))
- frappe.db.set(self, 'total_invoiced_amount', total)
+ total = sum(flt(d.grand_total) for d in self.get("invoices"))
+ frappe.db.set(self, "total_invoiced_amount", total)
@frappe.whitelist()
def get_invoice_details(self, invoice_no):
- """ Pull details from invoices for referrence """
+ """Pull details from invoices for referrence"""
if invoice_no:
- inv = frappe.db.get_value("Sales Invoice", invoice_no,
- ["posting_date", "territory", "base_net_total", "base_grand_total"], as_dict=True)
+ inv = frappe.db.get_value(
+ "Sales Invoice",
+ invoice_no,
+ ["posting_date", "territory", "base_net_total", "base_grand_total"],
+ as_dict=True,
+ )
return {
- 'invoice_date' : inv.posting_date,
- 'territory' : inv.territory,
- 'net_total' : inv.base_net_total,
- 'grand_total' : inv.base_grand_total
+ "invoice_date": inv.posting_date,
+ "territory": inv.territory,
+ "net_total": inv.base_net_total,
+ "grand_total": inv.base_grand_total,
}
diff --git a/erpnext/accounts/doctype/c_form/test_c_form.py b/erpnext/accounts/doctype/c_form/test_c_form.py
index fa34c255c661..87ad60fddace 100644
--- a/erpnext/accounts/doctype/c_form/test_c_form.py
+++ b/erpnext/accounts/doctype/c_form/test_c_form.py
@@ -5,5 +5,6 @@
# test_records = frappe.get_test_records('C-Form')
+
class TestCForm(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
index 6e7b687c04d6..79feb2dae234 100644
--- a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
+++ b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
@@ -1,25 +1,25 @@
DEFAULT_MAPPERS = [
- {
- 'doctype': 'Cash Flow Mapper',
- 'section_footer': 'Net cash generated by operating activities',
- 'section_header': 'Cash flows from operating activities',
- 'section_leader': 'Adjustments for',
- 'section_name': 'Operating Activities',
- 'position': 0,
- 'section_subtotal': 'Cash generated from operations',
- },
- {
- 'doctype': 'Cash Flow Mapper',
- 'position': 1,
- 'section_footer': 'Net cash used in investing activities',
- 'section_header': 'Cash flows from investing activities',
- 'section_name': 'Investing Activities'
- },
- {
- 'doctype': 'Cash Flow Mapper',
- 'position': 2,
- 'section_footer': 'Net cash used in financing activites',
- 'section_header': 'Cash flows from financing activities',
- 'section_name': 'Financing Activities',
- }
+ {
+ "doctype": "Cash Flow Mapper",
+ "section_footer": "Net cash generated by operating activities",
+ "section_header": "Cash flows from operating activities",
+ "section_leader": "Adjustments for",
+ "section_name": "Operating Activities",
+ "position": 0,
+ "section_subtotal": "Cash generated from operations",
+ },
+ {
+ "doctype": "Cash Flow Mapper",
+ "position": 1,
+ "section_footer": "Net cash used in investing activities",
+ "section_header": "Cash flows from investing activities",
+ "section_name": "Investing Activities",
+ },
+ {
+ "doctype": "Cash Flow Mapper",
+ "position": 2,
+ "section_footer": "Net cash used in financing activites",
+ "section_header": "Cash flows from financing activities",
+ "section_name": "Financing Activities",
+ },
]
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
index cd8381a4bd3f..3bce4d51c7aa 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
@@ -11,9 +11,11 @@ def validate(self):
self.validate_checked_options()
def validate_checked_options(self):
- checked_fields = [d for d in self.meta.fields if d.fieldtype == 'Check' and self.get(d.fieldname) == 1]
+ checked_fields = [
+ d for d in self.meta.fields if d.fieldtype == "Check" and self.get(d.fieldname) == 1
+ ]
if len(checked_fields) > 1:
frappe.throw(
- frappe._('You can only select a maximum of one option from the list of check boxes.'),
- title='Error'
+ frappe._("You can only select a maximum of one option from the list of check boxes."),
+ title="Error",
)
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
index abb25670467e..19f2425b4ce8 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
@@ -9,19 +9,16 @@
class TestCashFlowMapping(unittest.TestCase):
def setUp(self):
if frappe.db.exists("Cash Flow Mapping", "Test Mapping"):
- frappe.delete_doc('Cash Flow Mappping', 'Test Mapping')
+ frappe.delete_doc("Cash Flow Mappping", "Test Mapping")
def tearDown(self):
- frappe.delete_doc('Cash Flow Mapping', 'Test Mapping')
+ frappe.delete_doc("Cash Flow Mapping", "Test Mapping")
def test_multiple_selections_not_allowed(self):
- doc = frappe.new_doc('Cash Flow Mapping')
- doc.mapping_name = 'Test Mapping'
- doc.label = 'Test label'
- doc.append(
- 'accounts',
- {'account': 'Accounts Receivable - _TC'}
- )
+ doc = frappe.new_doc("Cash Flow Mapping")
+ doc.mapping_name = "Test Mapping"
+ doc.label = "Test label"
+ doc.append("accounts", {"account": "Accounts Receivable - _TC"})
doc.is_working_capital = 1
doc.is_finance_cost = 1
diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
index 9fbd0c97c1e7..60138077286f 100644
--- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
+++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
@@ -17,11 +17,14 @@ def before_save(self):
self.make_calculations()
def get_outstanding(self):
- values = frappe.db.sql("""
+ values = frappe.db.sql(
+ """
select sum(outstanding_amount)
from `tabSales Invoice`
where posting_date=%s and posting_time>=%s and posting_time<=%s and owner=%s
- """, (self.date, self.from_time, self.time, self.user))
+ """,
+ (self.date, self.from_time, self.time, self.user),
+ )
self.outstanding_amount = flt(values[0][0] if values else 0)
def make_calculations(self):
@@ -29,7 +32,9 @@ def make_calculations(self):
for i in self.payments:
total += flt(i.amount)
- self.net_amount = total + self.outstanding_amount + flt(self.expense) - flt(self.custody) + flt(self.returns)
+ self.net_amount = (
+ total + self.outstanding_amount + flt(self.expense) - flt(self.custody) + flt(self.returns)
+ )
def validate_time(self):
if self.from_time >= self.time:
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index aaacce4eb9de..01bf1c23e92e 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -25,33 +25,41 @@
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):
if not data:
- frappe.throw(_('No data found. Seems like you uploaded a blank file'))
+ frappe.throw(_("No data found. Seems like you uploaded a blank file"))
no_of_columns = max([len(d) for d in data])
if no_of_columns > 7:
- frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'),
- title=(_("Wrong Template")))
+ frappe.throw(
+ _("More columns found than expected. Please compare the uploaded file with standard template"),
+ title=(_("Wrong Template")),
+ )
+
@frappe.whitelist()
def validate_company(company):
- parent_company, allow_account_creation_against_child_company = frappe.db.get_value('Company',
- {'name': company}, ['parent_company',
- 'allow_account_creation_against_child_company'])
+ parent_company, allow_account_creation_against_child_company = frappe.db.get_value(
+ "Company", {"name": company}, ["parent_company", "allow_account_creation_against_child_company"]
+ )
if parent_company and (not allow_account_creation_against_child_company):
msg = _("{} is a child company.").format(frappe.bold(company)) + " "
msg += _("Please import accounts against parent company or enable {} in company master.").format(
- frappe.bold('Allow Account Creation Against Child Company'))
- frappe.throw(msg, title=_('Wrong Company'))
+ frappe.bold("Allow Account Creation Against Child Company")
+ )
+ frappe.throw(msg, title=_("Wrong Company"))
- if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
+ if frappe.db.get_all("GL Entry", {"company": company}, "name", limit=1):
return False
+
@frappe.whitelist()
def import_coa(file_name, company):
# delete existing data for accounts
@@ -60,7 +68,7 @@ def import_coa(file_name, company):
# create accounts
file_doc, extension = get_file(file_name)
- if extension == 'csv':
+ if extension == "csv":
data = generate_data_from_csv(file_doc)
else:
data = generate_data_from_excel(file_doc, extension)
@@ -72,27 +80,33 @@ def import_coa(file_name, company):
# trigger on_update for company to reset default accounts
set_default_accounts(company)
+
def get_file(file_name):
file_doc = frappe.get_doc("File", {"file_url": file_name})
parts = file_doc.get_extension()
extension = parts[1]
extension = extension.lstrip(".")
- if extension not in ('csv', 'xlsx', 'xls'):
- frappe.throw(_("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload"))
+ if extension not in ("csv", "xlsx", "xls"):
+ frappe.throw(
+ _(
+ "Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload"
+ )
+ )
+
+ return file_doc, extension
- return file_doc, extension
def generate_data_from_csv(file_doc, as_dict=False):
- ''' read csv file and return the generated nested tree '''
+ """read csv file and return the generated nested tree"""
file_path = file_doc.get_full_path()
data = []
- with open(file_path, 'r') as in_file:
+ with open(file_path, "r") as in_file:
csv_reader = list(csv.reader(in_file))
headers = csv_reader[0]
- del csv_reader[0] # delete top row and headers row
+ del csv_reader[0] # delete top row and headers row
for row in csv_reader:
if as_dict:
@@ -106,6 +120,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
# convert csv data
return data
+
def generate_data_from_excel(file_doc, extension, as_dict=False):
content = file_doc.get_content()
@@ -123,20 +138,21 @@ def generate_data_from_excel(file_doc, extension, as_dict=False):
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
else:
if not row[1]:
- row[1] = row[0]
- row[3] = row[2]
+ row[1] = row[0]
+ row[3] = row[2]
data.append(row)
return data
+
@frappe.whitelist()
def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
- ''' called by tree view (to fetch node's children) '''
+ """called by tree view (to fetch node's children)"""
file_doc, extension = get_file(file_name)
- parent = None if parent==_('All Accounts') else parent
+ parent = None if parent == _("All Accounts") else parent
- if extension == 'csv':
+ if extension == "csv":
data = generate_data_from_csv(file_doc)
else:
data = generate_data_from_excel(file_doc, extension)
@@ -146,32 +162,33 @@ def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
if not for_validate:
forest = build_forest(data)
- accounts = build_tree_from_json("", chart_data=forest, from_coa_importer=True) # returns a list of dict in a tree render-able form
+ accounts = build_tree_from_json(
+ "", chart_data=forest, from_coa_importer=True
+ ) # returns a list of dict in a tree render-able form
# filter out to show data for the selected node only
- accounts = [d for d in accounts if d['parent_account']==parent]
+ accounts = [d for d in accounts if d["parent_account"] == parent]
return accounts
else:
- return {
- 'show_import_button': 1
- }
+ return {"show_import_button": 1}
+
def build_forest(data):
- '''
- converts list of list into a nested tree
- if a = [[1,1], [1,2], [3,2], [4,4], [5,4]]
- tree = {
- 1: {
- 2: {
- 3: {}
- }
- },
- 4: {
- 5: {}
- }
- }
- '''
+ """
+ converts list of list into a nested tree
+ if a = [[1,1], [1,2], [3,2], [4,4], [5,4]]
+ tree = {
+ 1: {
+ 2: {
+ 3: {}
+ }
+ },
+ 4: {
+ 5: {}
+ }
+ }
+ """
# set the value of nested dictionary
def set_nested(d, path, value):
@@ -195,8 +212,11 @@ def return_parent(data, child):
elif account_name == child:
parent_account_list = return_parent(data, parent_account)
if not parent_account_list and parent_account:
- frappe.throw(_("The parent account {0} does not exists in the uploaded template").format(
- frappe.bold(parent_account)))
+ frappe.throw(
+ _("The parent account {0} does not exists in the uploaded template").format(
+ frappe.bold(parent_account)
+ )
+ )
return [child] + parent_account_list
charts_map, paths = {}, []
@@ -205,7 +225,15 @@ def return_parent(data, child):
error_messages = []
for i in data:
- account_name, parent_account, account_number, parent_account_number, is_group, account_type, root_type = i
+ (
+ account_name,
+ parent_account,
+ account_number,
+ parent_account_number,
+ is_group,
+ account_type,
+ root_type,
+ ) = i
if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
@@ -216,13 +244,17 @@ def return_parent(data, child):
account_name = "{} - {}".format(account_number, account_name)
charts_map[account_name] = {}
- charts_map[account_name]['account_name'] = name
- if account_number: charts_map[account_name]["account_number"] = account_number
- if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
- if account_type: charts_map[account_name]["account_type"] = account_type
- if root_type: charts_map[account_name]["root_type"] = root_type
+ charts_map[account_name]["account_name"] = name
+ if account_number:
+ charts_map[account_name]["account_number"] = account_number
+ if cint(is_group) == 1:
+ charts_map[account_name]["is_group"] = is_group
+ if account_type:
+ charts_map[account_name]["account_type"] = account_type
+ if root_type:
+ charts_map[account_name]["root_type"] = root_type
path = return_parent(data, account_name)[::-1]
- paths.append(path) # List of path is created
+ paths.append(path) # List of path is created
line_no += 1
if error_messages:
@@ -231,27 +263,32 @@ def return_parent(data, child):
out = {}
for path in paths:
for n, account_name in enumerate(path):
- set_nested(out, path[:n+1], charts_map[account_name]) # setting the value of nested dictionary.
+ set_nested(
+ out, path[: n + 1], charts_map[account_name]
+ ) # setting the value of nested dictionary.
return out
+
def build_response_as_excel(writer):
filename = frappe.generate_hash("", 10)
- with open(filename, 'wb') as f:
- f.write(cstr(writer.getvalue()).encode('utf-8'))
+ with open(filename, "wb") as f:
+ f.write(cstr(writer.getvalue()).encode("utf-8"))
f = open(filename)
reader = csv.reader(f)
from frappe.utils.xlsxutils import make_xlsx
+
xlsx_file = make_xlsx(reader, "Chart of Accounts Importer Template")
f.close()
os.remove(filename)
# write out response as a xlsx type
- frappe.response['filename'] = 'coa_importer_template.xlsx'
- frappe.response['filecontent'] = xlsx_file.getvalue()
- frappe.response['type'] = 'binary'
+ frappe.response["filename"] = "coa_importer_template.xlsx"
+ frappe.response["filecontent"] = xlsx_file.getvalue()
+ frappe.response["type"] = "binary"
+
@frappe.whitelist()
def download_template(file_type, template_type):
@@ -259,34 +296,46 @@ def download_template(file_type, template_type):
writer = get_template(template_type)
- if file_type == 'CSV':
+ if file_type == "CSV":
# download csv file
- frappe.response['result'] = cstr(writer.getvalue())
- frappe.response['type'] = 'csv'
- frappe.response['doctype'] = 'Chart of Accounts Importer'
+ frappe.response["result"] = cstr(writer.getvalue())
+ frappe.response["type"] = "csv"
+ frappe.response["doctype"] = "Chart of Accounts Importer"
else:
build_response_as_excel(writer)
+
def get_template(template_type):
- fields = ["Account Name", "Parent Account", "Account Number", "Parent Account Number", "Is Group", "Account Type", "Root Type"]
+ fields = [
+ "Account Name",
+ "Parent Account",
+ "Account Number",
+ "Parent Account Number",
+ "Is Group",
+ "Account Type",
+ "Root Type",
+ ]
writer = UnicodeWriter()
writer.writerow(fields)
- if template_type == 'Blank Template':
- for root_type in get_root_types():
- writer.writerow(['', '', '', 1, '', root_type])
+ if template_type == "Blank Template":
+ for root_type in get_root_types():
+ writer.writerow(["", "", "", 1, "", root_type])
for account in get_mandatory_group_accounts():
- writer.writerow(['', '', '', 1, account, "Asset"])
+ writer.writerow(["", "", "", 1, account, "Asset"])
for account_type in get_mandatory_account_types():
- writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')])
+ writer.writerow(
+ ["", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
+ )
else:
writer = get_sample_template(writer)
return writer
+
def get_sample_template(writer):
template = [
["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
@@ -316,7 +365,7 @@ def get_sample_template(writer):
@frappe.whitelist()
def validate_accounts(file_doc, extension):
- if extension == 'csv':
+ if extension == "csv":
accounts = generate_data_from_csv(file_doc, as_dict=True)
else:
accounts = generate_data_from_excel(file_doc, extension, as_dict=True)
@@ -325,7 +374,9 @@ def validate_accounts(file_doc, extension):
for account in accounts:
accounts_dict.setdefault(account["account_name"], account)
if "parent_account" not in account:
- msg = _("Please make sure the file you are using has 'Parent Account' column present in the header.")
+ msg = _(
+ "Please make sure the file you are using has 'Parent Account' column present in the header."
+ )
msg += "
"
msg += _("Alternatively, you can download the template and fill your data in.")
frappe.throw(msg, title=_("Parent Account Missing"))
@@ -336,77 +387,106 @@ def validate_accounts(file_doc, extension):
return [True, len(accounts)]
+
def validate_root(accounts):
- roots = [accounts[d] for d in accounts if not accounts[d].get('parent_account')]
+ roots = [accounts[d] for d in accounts if not accounts[d].get("parent_account")]
error_messages = []
for account in roots:
if not account.get("root_type") and account.get("account_name"):
- error_messages.append(_("Please enter Root Type for account- {0}").format(account.get("account_name")))
+ error_messages.append(
+ _("Please enter Root Type for account- {0}").format(account.get("account_name"))
+ )
elif account.get("root_type") not in get_root_types() and account.get("account_name"):
- error_messages.append(_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(account.get("account_name")))
+ error_messages.append(
+ _("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(
+ account.get("account_name")
+ )
+ )
validate_missing_roots(roots)
if error_messages:
frappe.throw("
".join(error_messages))
+
def validate_missing_roots(roots):
- root_types_added = set(d.get('root_type') for d in roots)
+ root_types_added = set(d.get("root_type") for d in roots)
missing = list(set(get_root_types()) - root_types_added)
if missing:
- frappe.throw(_("Please add Root Account for - {0}").format(' , '.join(missing)))
+ frappe.throw(_("Please add Root Account for - {0}").format(" , ".join(missing)))
+
def get_root_types():
- return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
+ return ("Asset", "Liability", "Expense", "Income", "Equity")
+
def get_report_type(root_type):
- if root_type in ('Asset', 'Liability', 'Equity'):
- return 'Balance Sheet'
+ if root_type in ("Asset", "Liability", "Equity"):
+ return "Balance Sheet"
else:
- return 'Profit and Loss'
+ return "Profit and Loss"
+
def get_mandatory_group_accounts():
- return ('Bank', 'Cash', 'Stock')
+ return ("Bank", "Cash", "Stock")
+
def get_mandatory_account_types():
return [
- {'account_type': 'Cost of Goods Sold', 'root_type': 'Expense'},
- {'account_type': 'Depreciation', 'root_type': 'Expense'},
- {'account_type': 'Fixed Asset', 'root_type': 'Asset'},
- {'account_type': 'Payable', 'root_type': 'Liability'},
- {'account_type': 'Receivable', 'root_type': 'Asset'},
- {'account_type': 'Stock Adjustment', 'root_type': 'Expense'},
- {'account_type': 'Bank', 'root_type': 'Asset'},
- {'account_type': 'Cash', 'root_type': 'Asset'},
- {'account_type': 'Stock', 'root_type': 'Asset'}
+ {"account_type": "Cost of Goods Sold", "root_type": "Expense"},
+ {"account_type": "Depreciation", "root_type": "Expense"},
+ {"account_type": "Fixed Asset", "root_type": "Asset"},
+ {"account_type": "Payable", "root_type": "Liability"},
+ {"account_type": "Receivable", "root_type": "Asset"},
+ {"account_type": "Stock Adjustment", "root_type": "Expense"},
+ {"account_type": "Bank", "root_type": "Asset"},
+ {"account_type": "Cash", "root_type": "Asset"},
+ {"account_type": "Stock", "root_type": "Asset"},
]
+
def unset_existing_data(company):
- linked = frappe.db.sql('''select fieldname from tabDocField
- where fieldtype="Link" and options="Account" and parent="Company"''', as_dict=True)
+ linked = frappe.db.sql(
+ '''select fieldname from tabDocField
+ where fieldtype="Link" and options="Account" and parent="Company"''',
+ as_dict=True,
+ )
# remove accounts data from company
- update_values = {d.fieldname: '' for d in linked}
- frappe.db.set_value('Company', company, update_values, update_values)
+ update_values = {d.fieldname: "" for d in linked}
+ frappe.db.set_value("Company", company, update_values, update_values)
# remove accounts data from various doctypes
- for doctype in ["Account", "Party Account", "Mode of Payment Account", "Tax Withholding Account",
- "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
- frappe.db.sql('''delete from `tab{0}` where `company`="%s"''' # nosec
- .format(doctype) % (company))
+ for doctype in [
+ "Account",
+ "Party Account",
+ "Mode of Payment Account",
+ "Tax Withholding Account",
+ "Sales Taxes and Charges Template",
+ "Purchase Taxes and Charges Template",
+ ]:
+ frappe.db.sql(
+ '''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company) # nosec
+ )
+
def set_default_accounts(company):
from erpnext.setup.doctype.company.company import install_country_fixtures
- company = frappe.get_doc('Company', company)
- company.update({
- "default_receivable_account": frappe.db.get_value("Account",
- {"company": company.name, "account_type": "Receivable", "is_group": 0}),
- "default_payable_account": frappe.db.get_value("Account",
- {"company": company.name, "account_type": "Payable", "is_group": 0})
- })
+
+ company = frappe.get_doc("Company", company)
+ company.update(
+ {
+ "default_receivable_account": frappe.db.get_value(
+ "Account", {"company": company.name, "account_type": "Receivable", "is_group": 0}
+ ),
+ "default_payable_account": frappe.db.get_value(
+ "Account", {"company": company.name, "account_type": "Payable", "is_group": 0}
+ ),
+ }
+ )
company.save()
install_country_fixtures(company.name, company.country)
diff --git a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
index 20cb42c109cf..f8ac66444b4d 100644
--- a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
+++ b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
@@ -10,17 +10,20 @@
class ChequePrintTemplate(Document):
pass
+
@frappe.whitelist()
def create_or_update_cheque_print_format(template_name):
if not frappe.db.exists("Print Format", template_name):
cheque_print = frappe.new_doc("Print Format")
- cheque_print.update({
- "doc_type": "Payment Entry",
- "standard": "No",
- "custom_format": 1,
- "print_format_type": "Jinja",
- "name": template_name
- })
+ cheque_print.update(
+ {
+ "doc_type": "Payment Entry",
+ "standard": "No",
+ "custom_format": 1,
+ "print_format_type": "Jinja",
+ "name": template_name,
+ }
+ )
else:
cheque_print = frappe.get_doc("Print Format", template_name)
@@ -69,10 +72,12 @@ def create_or_update_cheque_print_format(template_name):
{{doc.company}}
-"""%{
- "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,
+""" % {
+ "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"),
@@ -89,7 +94,7 @@ def create_or_update_cheque_print_format(template_name):
"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
+ "signatory_from_left_edge": doc.signatory_from_left_edge,
}
cheque_print.save(ignore_permissions=True)
diff --git a/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py b/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py
index 2b323a9bf624..9b003ceaa3ec 100644
--- a/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py
+++ b/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py
@@ -5,5 +5,6 @@
# test_records = frappe.get_test_records('Cheque Print Template')
+
class TestChequePrintTemplate(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py
index 07cc0764e389..31055c3fb420 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center.py
@@ -10,11 +10,14 @@
class CostCenter(NestedSet):
- nsm_parent_field = 'parent_cost_center'
+ nsm_parent_field = "parent_cost_center"
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, None, self.company)
+
+ self.name = get_autoname_with_number(
+ self.cost_center_number, self.cost_center_name, None, self.company
+ )
def validate(self):
self.validate_mandatory()
@@ -28,9 +31,12 @@ def validate_mandatory(self):
def validate_parent_cost_center(self):
if self.parent_cost_center:
- if not frappe.db.get_value('Cost Center', self.parent_cost_center, 'is_group'):
- frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
- frappe.bold(self.parent_cost_center)))
+ if not frappe.db.get_value("Cost Center", self.parent_cost_center, "is_group"):
+ frappe.throw(
+ _("{0} is not a group node. Please select a group node as parent cost center").format(
+ frappe.bold(self.parent_cost_center)
+ )
+ )
@frappe.whitelist()
def convert_group_to_ledger(self):
@@ -48,7 +54,9 @@ def convert_ledger_to_group(self):
if self.if_allocation_exists_against_cost_center():
frappe.throw(_("Cost Center with Allocation records can not be converted to a group"))
if self.check_if_part_of_cost_center_allocation():
- frappe.throw(_("Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group"))
+ frappe.throw(
+ _("Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group")
+ )
if self.check_gle_exists():
frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
self.is_group = 1
@@ -59,24 +67,26 @@ def check_gle_exists(self):
return frappe.db.get_value("GL Entry", {"cost_center": self.name})
def check_if_child_exists(self):
- return frappe.db.sql("select name from `tabCost Center` where \
- parent_cost_center = %s and docstatus != 2", self.name)
+ return frappe.db.sql(
+ "select name from `tabCost Center` where \
+ parent_cost_center = %s and docstatus != 2",
+ self.name,
+ )
def if_allocation_exists_against_cost_center(self):
- return frappe.db.get_value("Cost Center Allocation", filters = {
- "main_cost_center": self.name,
- "docstatus": 1
- })
+ return frappe.db.get_value(
+ "Cost Center Allocation", filters={"main_cost_center": self.name, "docstatus": 1}
+ )
def check_if_part_of_cost_center_allocation(self):
- return frappe.db.get_value("Cost Center Allocation Percentage", filters = {
- "cost_center": self.name,
- "docstatus": 1
- })
+ return frappe.db.get_value(
+ "Cost Center Allocation Percentage", filters={"cost_center": self.name, "docstatus": 1}
+ )
def before_rename(self, olddn, newdn, merge=False):
# Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr
+
new_cost_center = get_name_with_abbr(newdn, self.company)
# Validate properties before merging
@@ -90,7 +100,9 @@ def after_rename(self, olddn, newdn, merge=False):
super(CostCenter, self).after_rename(olddn, newdn, merge)
if not merge:
- new_cost_center = frappe.db.get_value("Cost Center", newdn, ["cost_center_name", "cost_center_number"], as_dict=1)
+ new_cost_center = frappe.db.get_value(
+ "Cost Center", newdn, ["cost_center_name", "cost_center_number"], as_dict=1
+ )
# exclude company abbr
new_parts = newdn.split(" - ")[:-1]
@@ -99,7 +111,9 @@ def after_rename(self, olddn, newdn, merge=False):
if len(new_parts) == 1:
new_parts = newdn.split(" ")
if new_cost_center.cost_center_number != new_parts[0]:
- validate_field_number("Cost Center", self.name, new_parts[0], self.company, "cost_center_number")
+ validate_field_number(
+ "Cost Center", self.name, new_parts[0], self.company, "cost_center_number"
+ )
self.cost_center_number = new_parts[0]
self.db_set("cost_center_number", new_parts[0])
new_parts = new_parts[1:]
@@ -110,10 +124,12 @@ def after_rename(self, olddn, newdn, merge=False):
self.cost_center_name = cost_center_name
self.db_set("cost_center_name", cost_center_name)
+
def on_doctype_update():
frappe.db.add_index("Cost Center", ["lft", "rgt"])
+
def get_name_with_number(new_account, account_number):
if account_number and not new_account[0].isdigit():
new_account = account_number + " - " + new_account
- return new_account
\ No newline at end of file
+ return new_account
diff --git a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
index f524803f64cf..5059dc3cc0e8 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
@@ -3,11 +3,6 @@
def get_data():
return {
- 'fieldname': 'cost_center',
- 'reports': [
- {
- 'label': _('Reports'),
- 'items': ['Budget Variance Report', 'General Ledger']
- }
- ]
+ "fieldname": "cost_center",
+ "reports": [{"label": _("Reports"), "items": ["Budget Variance Report", "General Ledger"]}],
}
diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py
index ff50a2112410..2ec16092d4db 100644
--- a/erpnext/accounts/doctype/cost_center/test_cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py
@@ -5,24 +5,28 @@
import frappe
-test_records = frappe.get_test_records('Cost Center')
+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'}):
+ if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
frappe.get_doc(test_records[1]).insert()
- cost_center = frappe.get_doc({
- 'doctype': 'Cost Center',
- 'cost_center_name': '_Test Cost Center 3',
- 'parent_cost_center': '_Test Cost Center 2 - _TC',
- 'is_group': 0,
- 'company': '_Test Company'
- })
+ cost_center = frappe.get_doc(
+ {
+ "doctype": "Cost Center",
+ "cost_center_name": "_Test Cost Center 3",
+ "parent_cost_center": "_Test Cost Center 2 - _TC",
+ "is_group": 0,
+ "company": "_Test Company",
+ }
+ )
self.assertRaises(frappe.ValidationError, cost_center.save)
+
def create_cost_center(**args):
args = frappe._dict(args)
if args.cost_center_name:
diff --git a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py
index bad3fb4f96c3..d25016fe596c 100644
--- a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py
+++ b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py
@@ -9,15 +9,24 @@
class MainCostCenterCantBeChild(frappe.ValidationError):
pass
+
+
class InvalidMainCostCenter(frappe.ValidationError):
pass
+
+
class InvalidChildCostCenter(frappe.ValidationError):
pass
+
+
class WrongPercentageAllocation(frappe.ValidationError):
pass
+
+
class InvalidDateError(frappe.ValidationError):
pass
+
class CostCenterAllocation(Document):
def validate(self):
self.validate_total_allocation_percentage()
@@ -30,61 +39,96 @@ def validate_total_allocation_percentage(self):
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
# If exists ensure from date is set after posting date of last GLE
- last_gle_date = frappe.db.get_value("GL Entry",
+ last_gle_date = frappe.db.get_value(
+ "GL Entry",
{"cost_center": self.main_cost_center, "is_cancelled": 0},
- "posting_date", order_by="posting_date desc")
+ "posting_date",
+ order_by="posting_date desc",
+ )
if last_gle_date:
if getdate(self.valid_from) <= getdate(last_gle_date):
- frappe.throw(_("Valid From must be after {0} as last GL Entry against the cost center {1} posted on this date")
- .format(last_gle_date, self.main_cost_center), InvalidDateError)
+ frappe.throw(
+ _(
+ "Valid From must be after {0} as last GL Entry against the cost center {1} posted on this date"
+ ).format(last_gle_date, self.main_cost_center),
+ InvalidDateError,
+ )
def validate_backdated_allocation(self):
# Check if there are any future existing allocation records against the main cost center
# If exists, warn the user about it
- future_allocation = frappe.db.get_value("Cost Center Allocation", filters = {
- "main_cost_center": self.main_cost_center,
- "valid_from": (">=", self.valid_from),
- "name": ("!=", self.name),
- "docstatus": 1
- }, fieldname=['valid_from', 'name'], order_by='valid_from', as_dict=1)
+ future_allocation = frappe.db.get_value(
+ "Cost Center Allocation",
+ filters={
+ "main_cost_center": self.main_cost_center,
+ "valid_from": (">=", self.valid_from),
+ "name": ("!=", self.name),
+ "docstatus": 1,
+ },
+ fieldname=["valid_from", "name"],
+ order_by="valid_from",
+ as_dict=1,
+ )
if future_allocation:
- frappe.msgprint(_("Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}")
- .format(frappe.bold(future_allocation.name), frappe.bold(format_date(future_allocation.valid_from)),
- frappe.bold(format_date(add_days(future_allocation.valid_from, -1)))),
- title=_("Warning!"), indicator="orange", alert=1
+ frappe.msgprint(
+ _(
+ "Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}"
+ ).format(
+ frappe.bold(future_allocation.name),
+ frappe.bold(format_date(future_allocation.valid_from)),
+ frappe.bold(format_date(add_days(future_allocation.valid_from, -1))),
+ ),
+ title=_("Warning!"),
+ indicator="orange",
+ alert=1,
)
def validate_main_cost_center(self):
# Main cost center itself cannot be entered in child table
if self.main_cost_center in [d.cost_center for d in self.allocation_percentages]:
- frappe.throw(_("Main Cost Center {0} cannot be entered in the child table")
- .format(self.main_cost_center), MainCostCenterCantBeChild)
+ frappe.throw(
+ _("Main Cost Center {0} cannot be entered in the child table").format(self.main_cost_center),
+ MainCostCenterCantBeChild,
+ )
# If main cost center is used for allocation under any other cost center,
# allocation cannot be done against it
- parent = frappe.db.get_value("Cost Center Allocation Percentage", filters = {
- "cost_center": self.main_cost_center,
- "docstatus": 1
- }, fieldname='parent')
+ parent = frappe.db.get_value(
+ "Cost Center Allocation Percentage",
+ filters={"cost_center": self.main_cost_center, "docstatus": 1},
+ fieldname="parent",
+ )
if parent:
- frappe.throw(_("{0} cannot be used as a Main Cost Center because it has been used as child in Cost Center Allocation {1}")
- .format(self.main_cost_center, parent), InvalidMainCostCenter)
+ frappe.throw(
+ _(
+ "{0} cannot be used as a Main Cost Center because it has been used as child in Cost Center Allocation {1}"
+ ).format(self.main_cost_center, parent),
+ InvalidMainCostCenter,
+ )
def validate_child_cost_centers(self):
# Check if child cost center is used as main cost center in any existing allocation
- main_cost_centers = [d.main_cost_center for d in
- frappe.get_all("Cost Center Allocation", {'docstatus': 1}, 'main_cost_center')]
+ main_cost_centers = [
+ d.main_cost_center
+ for d in frappe.get_all("Cost Center Allocation", {"docstatus": 1}, "main_cost_center")
+ ]
for d in self.allocation_percentages:
if d.cost_center in main_cost_centers:
- frappe.throw(_("Cost Center {0} cannot be used for allocation as it is used as main cost center in other allocation record.")
- .format(d.cost_center), InvalidChildCostCenter)
\ No newline at end of file
+ frappe.throw(
+ _(
+ "Cost Center {0} cannot be used for allocation as it is used as main cost center in other allocation record."
+ ).format(d.cost_center),
+ InvalidChildCostCenter,
+ )
diff --git a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
index 9cf4c002172b..65784dbb6c72 100644
--- a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
+++ b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
@@ -19,33 +19,35 @@
class TestCostCenterAllocation(unittest.TestCase):
def setUp(self):
- cost_centers = ["Main Cost Center 1", "Main Cost Center 2", "Sub Cost Center 1", "Sub Cost Center 2"]
+ cost_centers = [
+ "Main Cost Center 1",
+ "Main Cost Center 2",
+ "Sub Cost Center 1",
+ "Sub Cost Center 2",
+ ]
for cc in cost_centers:
create_cost_center(cost_center_name=cc, company="_Test Company")
def test_gle_based_on_cost_center_allocation(self):
- cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
- {
- "Sub Cost Center 1 - _TC": 60,
- "Sub Cost Center 2 - _TC": 40
- }
+ cca = create_cost_center_allocation(
+ "_Test Company",
+ "Main Cost Center 1 - _TC",
+ {"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
)
- jv = make_journal_entry("_Test Cash - _TC", "Sales - _TC", 100,
- cost_center = "Main Cost Center 1 - _TC", submit=True)
+ jv = make_journal_entry(
+ "_Test Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True
+ )
- expected_values = [
- ["Sub Cost Center 1 - _TC", 0.0, 60],
- ["Sub Cost Center 2 - _TC", 0.0, 40]
- ]
+ expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]]
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gle)
.select(gle.cost_center, gle.debit, gle.credit)
- .where(gle.voucher_type == 'Journal Entry')
+ .where(gle.voucher_type == "Journal Entry")
.where(gle.voucher_no == jv.name)
- .where(gle.account == 'Sales - _TC')
+ .where(gle.account == "Sales - _TC")
.orderby(gle.cost_center)
).run(as_dict=1)
@@ -61,11 +63,11 @@ def test_gle_based_on_cost_center_allocation(self):
def test_main_cost_center_cant_be_child(self):
# Main cost center itself cannot be entered in child table
- cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
- {
- "Sub Cost Center 1 - _TC": 60,
- "Main Cost Center 1 - _TC": 40
- }, save=False
+ cca = create_cost_center_allocation(
+ "_Test Company",
+ "Main Cost Center 1 - _TC",
+ {"Sub Cost Center 1 - _TC": 60, "Main Cost Center 1 - _TC": 40},
+ save=False,
)
self.assertRaises(MainCostCenterCantBeChild, cca.save)
@@ -73,17 +75,14 @@ def test_main_cost_center_cant_be_child(self):
def test_invalid_main_cost_center(self):
# If main cost center is used for allocation under any other cost center,
# allocation cannot be done against it
- cca1 = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
- {
- "Sub Cost Center 1 - _TC": 60,
- "Sub Cost Center 2 - _TC": 40
- }
+ cca1 = create_cost_center_allocation(
+ "_Test Company",
+ "Main Cost Center 1 - _TC",
+ {"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
)
- cca2 = create_cost_center_allocation("_Test Company", "Sub Cost Center 1 - _TC",
- {
- "Sub Cost Center 2 - _TC": 100
- }, save=False
+ cca2 = create_cost_center_allocation(
+ "_Test Company", "Sub Cost Center 1 - _TC", {"Sub Cost Center 2 - _TC": 100}, save=False
)
self.assertRaises(InvalidMainCostCenter, cca2.save)
@@ -92,18 +91,17 @@ def test_invalid_main_cost_center(self):
def test_if_child_cost_center_has_any_allocation_record(self):
# Check if any child cost center is used as main cost center in any other existing allocation
- cca1 = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
- {
- "Sub Cost Center 1 - _TC": 60,
- "Sub Cost Center 2 - _TC": 40
- }
+ cca1 = create_cost_center_allocation(
+ "_Test Company",
+ "Main Cost Center 1 - _TC",
+ {"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
)
- cca2 = create_cost_center_allocation("_Test Company", "Main Cost Center 2 - _TC",
- {
- "Main Cost Center 1 - _TC": 60,
- "Sub Cost Center 1 - _TC": 40
- }, save=False
+ cca2 = create_cost_center_allocation(
+ "_Test Company",
+ "Main Cost Center 2 - _TC",
+ {"Main Cost Center 1 - _TC": 60, "Sub Cost Center 1 - _TC": 40},
+ save=False,
)
self.assertRaises(InvalidChildCostCenter, cca2.save)
@@ -111,46 +109,58 @@ def test_if_child_cost_center_has_any_allocation_record(self):
cca1.cancel()
def test_total_percentage(self):
- cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
- {
- "Sub Cost Center 1 - _TC": 40,
- "Sub Cost Center 2 - _TC": 40
- }, save=False
+ cca = create_cost_center_allocation(
+ "_Test Company",
+ "Main Cost Center 1 - _TC",
+ {"Sub Cost Center 1 - _TC": 40, "Sub Cost Center 2 - _TC": 40},
+ save=False,
)
self.assertRaises(WrongPercentageAllocation, cca.save)
def test_valid_from_based_on_existing_gle(self):
# GLE posted against Sub Cost Center 1 on today
- jv = make_journal_entry("_Test Cash - _TC", "Sales - _TC", 100,
- cost_center = "Main Cost Center 1 - _TC", posting_date=today(), submit=True)
+ jv = make_journal_entry(
+ "_Test Cash - _TC",
+ "Sales - _TC",
+ 100,
+ cost_center="Main Cost Center 1 - _TC",
+ posting_date=today(),
+ submit=True,
+ )
# try to set valid from as yesterday
- cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
- {
- "Sub Cost Center 1 - _TC": 60,
- "Sub Cost Center 2 - _TC": 40
- }, valid_from=add_days(today(), -1), save=False
+ cca = create_cost_center_allocation(
+ "_Test Company",
+ "Main Cost Center 1 - _TC",
+ {"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
+ valid_from=add_days(today(), -1),
+ save=False,
)
self.assertRaises(InvalidDateError, cca.save)
jv.cancel()
-def create_cost_center_allocation(company, main_cost_center, allocation_percentages,
- valid_from=None, valid_upto=None, save=True, submit=True):
+
+def create_cost_center_allocation(
+ company,
+ main_cost_center,
+ allocation_percentages,
+ valid_from=None,
+ valid_upto=None,
+ save=True,
+ submit=True,
+):
doc = frappe.new_doc("Cost Center Allocation")
doc.main_cost_center = main_cost_center
doc.company = company
doc.valid_from = valid_from or today()
doc.valid_upto = valid_upto
for cc, percentage in allocation_percentages.items():
- doc.append("allocation_percentages", {
- "cost_center": cc,
- "percentage": percentage
- })
+ doc.append("allocation_percentages", {"cost_center": cc, "percentage": percentage})
if save:
doc.save()
if submit:
doc.submit()
- return doc
\ No newline at end of file
+ return doc
diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py
index ee32de1cd28e..6a0cdf91c0a4 100644
--- a/erpnext/accounts/doctype/coupon_code/coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py
@@ -15,7 +15,7 @@ def autoname(self):
if not self.coupon_code:
if self.coupon_type == "Promotional":
- self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
+ self.coupon_code = "".join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
elif self.coupon_type == "Gift Card":
self.coupon_code = frappe.generate_hash()[:10].upper()
diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
index ca482c8c4eca..b897546b0364 100644
--- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
@@ -7,92 +7,110 @@
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-test_dependencies = ['Item']
+test_dependencies = ["Item"]
+
def test_create_test_data():
frappe.set_user("Administrator")
# create test item
- if not frappe.db.exists("Item","_Test Tesla Car"):
- item = frappe.get_doc({
- "description": "_Test Tesla Car",
- "doctype": "Item",
- "has_batch_no": 0,
- "has_serial_no": 0,
- "inspection_required": 0,
- "is_stock_item": 1,
- "opening_stock":100,
- "is_sub_contracted_item": 0,
- "item_code": "_Test Tesla Car",
- "item_group": "_Test Item Group",
- "item_name": "_Test Tesla Car",
- "apply_warehouse_wise_reorder_level": 0,
- "warehouse":"Stores - _TC",
- "gst_hsn_code": "999800",
- "valuation_rate": 5000,
- "standard_rate":5000,
- "item_defaults": [{
- "company": "_Test Company",
- "default_warehouse": "Stores - _TC",
- "default_price_list":"_Test Price List",
- "expense_account": "Cost of Goods Sold - _TC",
- "buying_cost_center": "Main - _TC",
- "selling_cost_center": "Main - _TC",
- "income_account": "Sales - _TC"
- }],
- })
+ if not frappe.db.exists("Item", "_Test Tesla Car"):
+ item = frappe.get_doc(
+ {
+ "description": "_Test Tesla Car",
+ "doctype": "Item",
+ "has_batch_no": 0,
+ "has_serial_no": 0,
+ "inspection_required": 0,
+ "is_stock_item": 1,
+ "opening_stock": 100,
+ "is_sub_contracted_item": 0,
+ "item_code": "_Test Tesla Car",
+ "item_group": "_Test Item Group",
+ "item_name": "_Test Tesla Car",
+ "apply_warehouse_wise_reorder_level": 0,
+ "warehouse": "Stores - _TC",
+ "gst_hsn_code": "999800",
+ "valuation_rate": 5000,
+ "standard_rate": 5000,
+ "item_defaults": [
+ {
+ "company": "_Test Company",
+ "default_warehouse": "Stores - _TC",
+ "default_price_list": "_Test Price List",
+ "expense_account": "Cost of Goods Sold - _TC",
+ "buying_cost_center": "Main - _TC",
+ "selling_cost_center": "Main - _TC",
+ "income_account": "Sales - _TC",
+ }
+ ],
+ }
+ )
item.insert()
# create test item price
- item_price = frappe.get_list('Item Price', filters={'item_code': '_Test Tesla Car', 'price_list': '_Test Price List'}, fields=['name'])
- if len(item_price)==0:
- item_price = frappe.get_doc({
- "doctype": "Item Price",
- "item_code": "_Test Tesla Car",
- "price_list": "_Test Price List",
- "price_list_rate": 5000
- })
+ item_price = frappe.get_list(
+ "Item Price",
+ filters={"item_code": "_Test Tesla Car", "price_list": "_Test Price List"},
+ fields=["name"],
+ )
+ if len(item_price) == 0:
+ item_price = frappe.get_doc(
+ {
+ "doctype": "Item Price",
+ "item_code": "_Test Tesla Car",
+ "price_list": "_Test Price List",
+ "price_list_rate": 5000,
+ }
+ )
item_price.insert()
# create test item pricing rule
if not frappe.db.exists("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}):
- item_pricing_rule = frappe.get_doc({
- "doctype": "Pricing Rule",
- "title": "_Test Pricing Rule for _Test Item",
- "apply_on": "Item Code",
- "items": [{
- "item_code": "_Test Tesla Car"
- }],
- "warehouse":"Stores - _TC",
- "coupon_code_based":1,
- "selling": 1,
- "rate_or_discount": "Discount Percentage",
- "discount_percentage": 30,
- "company": "_Test Company",
- "currency":"INR",
- "for_price_list":"_Test Price List"
- })
+ item_pricing_rule = frappe.get_doc(
+ {
+ "doctype": "Pricing Rule",
+ "title": "_Test Pricing Rule for _Test Item",
+ "apply_on": "Item Code",
+ "items": [{"item_code": "_Test Tesla Car"}],
+ "warehouse": "Stores - _TC",
+ "coupon_code_based": 1,
+ "selling": 1,
+ "rate_or_discount": "Discount Percentage",
+ "discount_percentage": 30,
+ "company": "_Test Company",
+ "currency": "INR",
+ "for_price_list": "_Test Price List",
+ }
+ )
item_pricing_rule.insert()
# create test item sales partner
- if not frappe.db.exists("Sales Partner","_Test Coupon Partner"):
- sales_partner = frappe.get_doc({
- "doctype": "Sales Partner",
- "partner_name":"_Test Coupon Partner",
- "commission_rate":2,
- "referral_code": "COPART"
- })
+ if not frappe.db.exists("Sales Partner", "_Test Coupon Partner"):
+ sales_partner = frappe.get_doc(
+ {
+ "doctype": "Sales Partner",
+ "partner_name": "_Test Coupon Partner",
+ "commission_rate": 2,
+ "referral_code": "COPART",
+ }
+ )
sales_partner.insert()
# create test item coupon code
if not frappe.db.exists("Coupon Code", "SAVE30"):
- pricing_rule = frappe.db.get_value("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}, ['name'])
- coupon_code = frappe.get_doc({
- "doctype": "Coupon Code",
- "coupon_name":"SAVE30",
- "coupon_code":"SAVE30",
- "pricing_rule": pricing_rule,
- "valid_from": "2014-01-01",
- "maximum_use":1,
- "used":0
- })
+ pricing_rule = frappe.db.get_value(
+ "Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}, ["name"]
+ )
+ coupon_code = frappe.get_doc(
+ {
+ "doctype": "Coupon Code",
+ "coupon_name": "SAVE30",
+ "coupon_code": "SAVE30",
+ "pricing_rule": pricing_rule,
+ "valid_from": "2014-01-01",
+ "maximum_use": 1,
+ "used": 0,
+ }
+ )
coupon_code.insert()
+
class TestCouponCode(unittest.TestCase):
def setUp(self):
test_create_test_data()
@@ -103,15 +121,21 @@ def tearDown(self):
def test_sales_order_with_coupon_code(self):
frappe.db.set_value("Coupon Code", "SAVE30", "used", 0)
- so = make_sales_order(company='_Test Company', warehouse='Stores - _TC',
- customer="_Test Customer", selling_price_list="_Test Price List",
- item_code="_Test Tesla Car", rate=5000, qty=1,
- do_not_submit=True)
+ so = make_sales_order(
+ company="_Test Company",
+ warehouse="Stores - _TC",
+ customer="_Test Customer",
+ selling_price_list="_Test Price List",
+ item_code="_Test Tesla Car",
+ rate=5000,
+ qty=1,
+ do_not_submit=True,
+ )
self.assertEqual(so.items[0].rate, 5000)
- so.coupon_code='SAVE30'
- so.sales_partner='_Test Coupon Partner'
+ so.coupon_code = "SAVE30"
+ so.sales_partner = "_Test Coupon Partner"
so.save()
# check item price after coupon code is applied
diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
index e16ff3aa7eb2..04a8e8ea92f2 100644
--- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
+++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
@@ -15,24 +15,24 @@ def validate(self):
self.validate_result(response, value)
def set_parameters_and_result(self):
- if self.service_provider == 'exchangerate.host':
- self.set('result_key', [])
- self.set('req_params', [])
+ if self.service_provider == "exchangerate.host":
+ self.set("result_key", [])
+ self.set("req_params", [])
self.api_endpoint = "https://api.exchangerate.host/convert"
- self.append('result_key', {'key': 'result'})
- self.append('req_params', {'key': 'date', 'value': '{transaction_date}'})
- self.append('req_params', {'key': 'from', 'value': '{from_currency}'})
- self.append('req_params', {'key': 'to', 'value': '{to_currency}'})
- elif self.service_provider == 'frankfurter.app':
- self.set('result_key', [])
- self.set('req_params', [])
+ self.append("result_key", {"key": "result"})
+ self.append("req_params", {"key": "date", "value": "{transaction_date}"})
+ self.append("req_params", {"key": "from", "value": "{from_currency}"})
+ self.append("req_params", {"key": "to", "value": "{to_currency}"})
+ elif self.service_provider == "frankfurter.app":
+ self.set("result_key", [])
+ self.set("req_params", [])
self.api_endpoint = "https://frankfurter.app/{transaction_date}"
- self.append('result_key', {'key': 'rates'})
- self.append('result_key', {'key': '{to_currency}'})
- self.append('req_params', {'key': 'base', 'value': '{from_currency}'})
- self.append('req_params', {'key': 'symbols', 'value': '{to_currency}'})
+ self.append("result_key", {"key": "rates"})
+ self.append("result_key", {"key": "{to_currency}"})
+ self.append("req_params", {"key": "base", "value": "{from_currency}"})
+ self.append("req_params", {"key": "symbols", "value": "{to_currency}"})
def validate_parameters(self):
if frappe.flags.in_test:
@@ -41,15 +41,11 @@ def validate_parameters(self):
params = {}
for row in self.req_params:
params[row.key] = row.value.format(
- transaction_date=nowdate(),
- to_currency='INR',
- from_currency='USD'
+ transaction_date=nowdate(), to_currency="INR", from_currency="USD"
)
api_url = self.api_endpoint.format(
- transaction_date=nowdate(),
- to_currency='INR',
- from_currency='USD'
+ transaction_date=nowdate(), to_currency="INR", from_currency="USD"
)
try:
@@ -68,11 +64,9 @@ def validate_result(self, response, value):
try:
for key in self.result_key:
- value = value[str(key.key).format(
- transaction_date=nowdate(),
- to_currency='INR',
- from_currency='USD'
- )]
+ value = value[
+ str(key.key).format(transaction_date=nowdate(), to_currency="INR", from_currency="USD")
+ ]
except Exception:
frappe.throw("Invalid result key. Response: " + response.text)
if not isinstance(value, (int, float)):
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index 5da0077a2be8..9874d66fa553 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -19,78 +19,99 @@ def validate(self):
self.validate_overdue_days()
self.validate_amount()
if not self.income_account:
- self.income_account = frappe.db.get_value('Company', self.company, 'default_income_account')
+ self.income_account = frappe.db.get_value("Company", self.company, "default_income_account")
def validate_overdue_days(self):
self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0
def validate_amount(self):
amounts = calculate_interest_and_amount(
- self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days)
- if self.interest_amount != amounts.get('interest_amount'):
- self.interest_amount = flt(amounts.get('interest_amount'), self.precision('interest_amount'))
- if self.dunning_amount != amounts.get('dunning_amount'):
- self.dunning_amount = flt(amounts.get('dunning_amount'), self.precision('dunning_amount'))
- if self.grand_total != amounts.get('grand_total'):
- self.grand_total = flt(amounts.get('grand_total'), self.precision('grand_total'))
+ self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days
+ )
+ if self.interest_amount != amounts.get("interest_amount"):
+ self.interest_amount = flt(amounts.get("interest_amount"), self.precision("interest_amount"))
+ if self.dunning_amount != amounts.get("dunning_amount"):
+ self.dunning_amount = flt(amounts.get("dunning_amount"), self.precision("dunning_amount"))
+ if self.grand_total != amounts.get("grand_total"):
+ self.grand_total = flt(amounts.get("grand_total"), self.precision("grand_total"))
def on_submit(self):
self.make_gl_entries()
def on_cancel(self):
if self.dunning_amount:
- self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+ self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def make_gl_entries(self):
if not self.dunning_amount:
return
gl_entries = []
- invoice_fields = ["project", "cost_center", "debit_to", "party_account_currency", "conversion_rate", "cost_center"]
+ invoice_fields = [
+ "project",
+ "cost_center",
+ "debit_to",
+ "party_account_currency",
+ "conversion_rate",
+ "cost_center",
+ ]
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
accounting_dimensions = get_accounting_dimensions()
invoice_fields.extend(accounting_dimensions)
dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
- default_cost_center = frappe.get_cached_value('Company', self.company, 'cost_center')
+ default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
gl_entries.append(
- self.get_gl_dict({
- "account": inv.debit_to,
- "party_type": "Customer",
- "party": self.customer,
- "due_date": self.due_date,
- "against": self.income_account,
- "debit": dunning_in_company_currency,
- "debit_in_account_currency": self.dunning_amount,
- "against_voucher": self.name,
- "against_voucher_type": "Dunning",
- "cost_center": inv.cost_center or default_cost_center,
- "project": inv.project
- }, inv.party_account_currency, item=inv)
+ self.get_gl_dict(
+ {
+ "account": inv.debit_to,
+ "party_type": "Customer",
+ "party": self.customer,
+ "due_date": self.due_date,
+ "against": self.income_account,
+ "debit": dunning_in_company_currency,
+ "debit_in_account_currency": self.dunning_amount,
+ "against_voucher": self.name,
+ "against_voucher_type": "Dunning",
+ "cost_center": inv.cost_center or default_cost_center,
+ "project": inv.project,
+ },
+ inv.party_account_currency,
+ item=inv,
+ )
)
gl_entries.append(
- self.get_gl_dict({
- "account": self.income_account,
- "against": self.customer,
- "credit": dunning_in_company_currency,
- "cost_center": inv.cost_center or default_cost_center,
- "credit_in_account_currency": self.dunning_amount,
- "project": inv.project
- }, item=inv)
+ self.get_gl_dict(
+ {
+ "account": self.income_account,
+ "against": self.customer,
+ "credit": dunning_in_company_currency,
+ "cost_center": inv.cost_center or default_cost_center,
+ "credit_in_account_currency": self.dunning_amount,
+ "project": inv.project,
+ },
+ item=inv,
+ )
+ )
+ make_gl_entries(
+ gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
)
- make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False)
def resolve_dunning(doc, state):
for reference in doc.references:
- if reference.reference_doctype == 'Sales Invoice' and reference.outstanding_amount <= 0:
- dunnings = frappe.get_list('Dunning', filters={
- 'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}, ignore_permissions=True)
+ if reference.reference_doctype == "Sales Invoice" and reference.outstanding_amount <= 0:
+ dunnings = frappe.get_list(
+ "Dunning",
+ filters={"sales_invoice": reference.reference_name, "status": ("!=", "Resolved")},
+ ignore_permissions=True,
+ )
for dunning in dunnings:
- frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved')
+ frappe.db.set_value("Dunning", dunning.name, "status", "Resolved")
+
def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
interest_amount = 0
@@ -101,23 +122,26 @@ def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_
grand_total += flt(interest_amount)
dunning_amount = flt(interest_amount) + flt(dunning_fee)
return {
- 'interest_amount': interest_amount,
- 'grand_total': grand_total,
- 'dunning_amount': dunning_amount}
+ "interest_amount": interest_amount,
+ "grand_total": grand_total,
+ "dunning_amount": dunning_amount,
+ }
+
@frappe.whitelist()
def get_dunning_letter_text(dunning_type, doc, language=None):
if isinstance(doc, str):
doc = json.loads(doc)
if language:
- filters = {'parent': dunning_type, 'language': language}
+ filters = {"parent": dunning_type, "language": language}
else:
- filters = {'parent': dunning_type, 'is_default_language': 1}
- letter_text = frappe.db.get_value('Dunning Letter Text', filters,
- ['body_text', 'closing_text', 'language'], as_dict=1)
+ filters = {"parent": dunning_type, "is_default_language": 1}
+ letter_text = frappe.db.get_value(
+ "Dunning Letter Text", filters, ["body_text", "closing_text", "language"], as_dict=1
+ )
if letter_text:
return {
- 'body_text': frappe.render_template(letter_text.body_text, doc),
- 'closing_text': frappe.render_template(letter_text.closing_text, doc),
- 'language': letter_text.language
+ "body_text": frappe.render_template(letter_text.body_text, doc),
+ "closing_text": frappe.render_template(letter_text.closing_text, doc),
+ "language": letter_text.language,
}
diff --git a/erpnext/accounts/doctype/dunning/dunning_dashboard.py b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
index a891bd291716..d1d403141041 100644
--- a/erpnext/accounts/doctype/dunning/dunning_dashboard.py
+++ b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
@@ -3,15 +3,10 @@
def get_data():
return {
- 'fieldname': 'dunning',
- 'non_standard_fieldnames': {
- 'Journal Entry': 'reference_name',
- 'Payment Entry': 'reference_name'
+ "fieldname": "dunning",
+ "non_standard_fieldnames": {
+ "Journal Entry": "reference_name",
+ "Payment Entry": "reference_name",
},
- 'transactions': [
- {
- 'label': _('Payment'),
- 'items': ['Payment Entry', 'Journal Entry']
- }
- ]
+ "transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]}],
}
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index b043c5ba19ce..e1fd1e984f5b 100644
--- a/erpnext/accounts/doctype/dunning/test_dunning.py
+++ b/erpnext/accounts/doctype/dunning/test_dunning.py
@@ -30,30 +30,35 @@ def tearDownClass(self):
def test_dunning(self):
dunning = create_dunning()
amounts = calculate_interest_and_amount(
- dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
- self.assertEqual(round(amounts.get('interest_amount'), 2), 0.44)
- self.assertEqual(round(amounts.get('dunning_amount'), 2), 20.44)
- self.assertEqual(round(amounts.get('grand_total'), 2), 120.44)
+ dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
+ )
+ self.assertEqual(round(amounts.get("interest_amount"), 2), 0.44)
+ self.assertEqual(round(amounts.get("dunning_amount"), 2), 20.44)
+ self.assertEqual(round(amounts.get("grand_total"), 2), 120.44)
def test_dunning_with_zero_interest_rate(self):
dunning = create_dunning_with_zero_interest_rate()
amounts = calculate_interest_and_amount(
- dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
- self.assertEqual(round(amounts.get('interest_amount'), 2), 0)
- self.assertEqual(round(amounts.get('dunning_amount'), 2), 20)
- self.assertEqual(round(amounts.get('grand_total'), 2), 120)
+ dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
+ )
+ self.assertEqual(round(amounts.get("interest_amount"), 2), 0)
+ self.assertEqual(round(amounts.get("dunning_amount"), 2), 20)
+ self.assertEqual(round(amounts.get("grand_total"), 2), 120)
def test_gl_entries(self):
dunning = create_dunning()
dunning.submit()
- gl_entries = frappe.db.sql("""select account, debit, credit
+ gl_entries = frappe.db.sql(
+ """select account, debit, credit
from `tabGL Entry` where voucher_type='Dunning' and voucher_no=%s
- order by account asc""", dunning.name, as_dict=1)
+ order by account asc""",
+ dunning.name,
+ as_dict=1,
+ )
self.assertTrue(gl_entries)
- expected_values = dict((d[0], d) for d in [
- ['Debtors - _TC', 20.44, 0.0],
- ['Sales - _TC', 0.0, 20.44]
- ])
+ expected_values = dict(
+ (d[0], d) for d in [["Debtors - _TC", 20.44, 0.0], ["Sales - _TC", 0.0, 20.44]]
+ )
for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit)
@@ -71,7 +76,7 @@ def test_payment_entry(self):
pe.target_exchange_rate = 1
pe.insert()
pe.submit()
- si_doc = frappe.get_doc('Sales Invoice', dunning.sales_invoice)
+ si_doc = frappe.get_doc("Sales Invoice", dunning.sales_invoice)
self.assertEqual(si_doc.outstanding_amount, 0)
@@ -79,8 +84,9 @@ def create_dunning():
posting_date = add_days(today(), -20)
due_date = add_days(today(), -15)
sales_invoice = create_sales_invoice_against_cost_center(
- posting_date=posting_date, due_date=due_date, status='Overdue')
- dunning_type = frappe.get_doc("Dunning Type", 'First Notice')
+ posting_date=posting_date, due_date=due_date, status="Overdue"
+ )
+ dunning_type = frappe.get_doc("Dunning Type", "First Notice")
dunning = frappe.new_doc("Dunning")
dunning.sales_invoice = sales_invoice.name
dunning.customer_name = sales_invoice.customer_name
@@ -90,18 +96,20 @@ def create_dunning():
dunning.company = sales_invoice.company
dunning.posting_date = nowdate()
dunning.due_date = sales_invoice.due_date
- dunning.dunning_type = 'First Notice'
+ dunning.dunning_type = "First Notice"
dunning.rate_of_interest = dunning_type.rate_of_interest
dunning.dunning_fee = dunning_type.dunning_fee
dunning.save()
return dunning
+
def create_dunning_with_zero_interest_rate():
posting_date = add_days(today(), -20)
due_date = add_days(today(), -15)
sales_invoice = create_sales_invoice_against_cost_center(
- posting_date=posting_date, due_date=due_date, status='Overdue')
- dunning_type = frappe.get_doc("Dunning Type", 'First Notice with 0% Rate of Interest')
+ posting_date=posting_date, due_date=due_date, status="Overdue"
+ )
+ dunning_type = frappe.get_doc("Dunning Type", "First Notice with 0% Rate of Interest")
dunning = frappe.new_doc("Dunning")
dunning.sales_invoice = sales_invoice.name
dunning.customer_name = sales_invoice.customer_name
@@ -111,40 +119,44 @@ def create_dunning_with_zero_interest_rate():
dunning.company = sales_invoice.company
dunning.posting_date = nowdate()
dunning.due_date = sales_invoice.due_date
- dunning.dunning_type = 'First Notice with 0% Rate of Interest'
+ dunning.dunning_type = "First Notice with 0% Rate of Interest"
dunning.rate_of_interest = dunning_type.rate_of_interest
dunning.dunning_fee = dunning_type.dunning_fee
dunning.save()
return dunning
+
def create_dunning_type():
dunning_type = frappe.new_doc("Dunning Type")
- dunning_type.dunning_type = 'First Notice'
+ dunning_type.dunning_type = "First Notice"
dunning_type.start_day = 10
dunning_type.end_day = 20
dunning_type.dunning_fee = 20
dunning_type.rate_of_interest = 8
dunning_type.append(
- "dunning_letter_text", {
- 'language': 'en',
- 'body_text': 'We have still not received payment for our invoice ',
- 'closing_text': 'We kindly request that you pay the outstanding amount immediately, including interest and late fees.'
- }
+ "dunning_letter_text",
+ {
+ "language": "en",
+ "body_text": "We have still not received payment for our invoice ",
+ "closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees.",
+ },
)
dunning_type.save()
+
def create_dunning_type_with_zero_interest_rate():
dunning_type = frappe.new_doc("Dunning Type")
- dunning_type.dunning_type = 'First Notice with 0% Rate of Interest'
+ dunning_type.dunning_type = "First Notice with 0% Rate of Interest"
dunning_type.start_day = 10
dunning_type.end_day = 20
dunning_type.dunning_fee = 20
dunning_type.rate_of_interest = 0
dunning_type.append(
- "dunning_letter_text", {
- 'language': 'en',
- 'body_text': 'We have still not received payment for our invoice ',
- 'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.'
- }
+ "dunning_letter_text",
+ {
+ "language": "en",
+ "body_text": "We have still not received payment for our invoice ",
+ "closing_text": "We kindly request that you pay the outstanding amount immediately, and late fees.",
+ },
)
dunning_type.save()
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 1b13195ce988..2f81c5fb7504 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -20,8 +20,9 @@ def validate(self):
def set_total_gain_loss(self):
total_gain_loss = 0
for d in self.accounts:
- d.gain_loss = flt(d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")) \
- - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
+ d.gain_loss = flt(
+ d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")
+ ) - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
total_gain_loss += flt(d.gain_loss, d.precision("gain_loss"))
self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss"))
@@ -30,15 +31,15 @@ def validate_mandatory(self):
frappe.throw(_("Please select Company and Posting Date to getting entries"))
def on_cancel(self):
- self.ignore_linked_doctypes = ('GL Entry')
+ self.ignore_linked_doctypes = "GL Entry"
@frappe.whitelist()
def check_journal_entry_condition(self):
- total_debit = frappe.db.get_value("Journal Entry Account", {
- 'reference_type': 'Exchange Rate Revaluation',
- 'reference_name': self.name,
- 'docstatus': 1
- }, "sum(debit) as sum")
+ total_debit = frappe.db.get_value(
+ "Journal Entry Account",
+ {"reference_type": "Exchange Rate Revaluation", "reference_name": self.name, "docstatus": 1},
+ "sum(debit) as sum",
+ )
total_amt = 0
for d in self.accounts:
@@ -54,28 +55,33 @@ def get_accounts_data(self, account=None):
accounts = []
self.validate_mandatory()
company_currency = erpnext.get_company_currency(self.company)
- precision = get_field_precision(frappe.get_meta("Exchange Rate Revaluation Account")
- .get_field("new_balance_in_base_currency"), company_currency)
+ precision = get_field_precision(
+ frappe.get_meta("Exchange Rate Revaluation Account").get_field("new_balance_in_base_currency"),
+ company_currency,
+ )
account_details = self.get_accounts_from_gle()
for d in account_details:
- current_exchange_rate = d.balance / d.balance_in_account_currency \
- if d.balance_in_account_currency else 0
+ current_exchange_rate = (
+ d.balance / d.balance_in_account_currency if d.balance_in_account_currency else 0
+ )
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, self.posting_date)
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
if gain_loss:
- accounts.append({
- "account": d.account,
- "party_type": d.party_type,
- "party": d.party,
- "account_currency": d.account_currency,
- "balance_in_base_currency": d.balance,
- "balance_in_account_currency": d.balance_in_account_currency,
- "current_exchange_rate": current_exchange_rate,
- "new_exchange_rate": new_exchange_rate,
- "new_balance_in_base_currency": new_balance_in_base_currency
- })
+ accounts.append(
+ {
+ "account": d.account,
+ "party_type": d.party_type,
+ "party": d.party,
+ "account_currency": d.account_currency,
+ "balance_in_base_currency": d.balance,
+ "balance_in_account_currency": d.balance_in_account_currency,
+ "current_exchange_rate": current_exchange_rate,
+ "new_exchange_rate": new_exchange_rate,
+ "new_balance_in_base_currency": new_balance_in_base_currency,
+ }
+ )
if not accounts:
self.throw_invalid_response_message(account_details)
@@ -84,7 +90,8 @@ def get_accounts_data(self, account=None):
def get_accounts_from_gle(self):
company_currency = erpnext.get_company_currency(self.company)
- accounts = frappe.db.sql_list("""
+ accounts = frappe.db.sql_list(
+ """
select name
from tabAccount
where is_group = 0
@@ -93,11 +100,14 @@ def get_accounts_from_gle(self):
and account_type != 'Stock'
and company=%s
and account_currency != %s
- order by name""",(self.company, company_currency))
+ order by name""",
+ (self.company, company_currency),
+ )
account_details = []
if accounts:
- account_details = frappe.db.sql("""
+ account_details = frappe.db.sql(
+ """
select
account, party_type, party, account_currency,
sum(debit_in_account_currency) - sum(credit_in_account_currency) as balance_in_account_currency,
@@ -109,7 +119,11 @@ def get_accounts_from_gle(self):
group by account, NULLIF(party_type,''), NULLIF(party,'')
having sum(debit) != sum(credit)
order by account
- """ % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1)
+ """
+ % (", ".join(["%s"] * len(accounts)), "%s"),
+ tuple(accounts + [self.posting_date]),
+ as_dict=1,
+ )
return account_details
@@ -125,77 +139,107 @@ def make_jv_entry(self):
if self.total_gain_loss == 0:
return
- unrealized_exchange_gain_loss_account = frappe.get_cached_value('Company', self.company,
- "unrealized_exchange_gain_loss_account")
+ unrealized_exchange_gain_loss_account = frappe.get_cached_value(
+ "Company", self.company, "unrealized_exchange_gain_loss_account"
+ )
if not unrealized_exchange_gain_loss_account:
- frappe.throw(_("Please set Unrealized Exchange Gain/Loss Account in Company {0}")
- .format(self.company))
+ frappe.throw(
+ _("Please set Unrealized Exchange Gain/Loss Account in Company {0}").format(self.company)
+ )
- journal_entry = frappe.new_doc('Journal Entry')
- journal_entry.voucher_type = 'Exchange Rate Revaluation'
+ journal_entry = frappe.new_doc("Journal Entry")
+ journal_entry.voucher_type = "Exchange Rate Revaluation"
journal_entry.company = self.company
journal_entry.posting_date = self.posting_date
journal_entry.multi_currency = 1
journal_entry_accounts = []
for d in self.accounts:
- dr_or_cr = "debit_in_account_currency" \
- if d.get("balance_in_account_currency") > 0 else "credit_in_account_currency"
-
- reverse_dr_or_cr = "debit_in_account_currency" \
- if dr_or_cr=="credit_in_account_currency" else "credit_in_account_currency"
-
- journal_entry_accounts.append({
- "account": d.get("account"),
- "party_type": d.get("party_type"),
- "party": d.get("party"),
- "account_currency": d.get("account_currency"),
- "balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
- dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
- "exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
+ dr_or_cr = (
+ "debit_in_account_currency"
+ if d.get("balance_in_account_currency") > 0
+ else "credit_in_account_currency"
+ )
+
+ reverse_dr_or_cr = (
+ "debit_in_account_currency"
+ if dr_or_cr == "credit_in_account_currency"
+ else "credit_in_account_currency"
+ )
+
+ journal_entry_accounts.append(
+ {
+ "account": d.get("account"),
+ "party_type": d.get("party_type"),
+ "party": d.get("party"),
+ "account_currency": d.get("account_currency"),
+ "balance": flt(
+ d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
+ ),
+ dr_or_cr: flt(
+ abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
+ ),
+ "exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
+ "reference_type": "Exchange Rate Revaluation",
+ "reference_name": self.name,
+ }
+ )
+ journal_entry_accounts.append(
+ {
+ "account": d.get("account"),
+ "party_type": d.get("party_type"),
+ "party": d.get("party"),
+ "account_currency": d.get("account_currency"),
+ "balance": flt(
+ d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
+ ),
+ reverse_dr_or_cr: flt(
+ abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
+ ),
+ "exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
+ "reference_type": "Exchange Rate Revaluation",
+ "reference_name": self.name,
+ }
+ )
+
+ journal_entry_accounts.append(
+ {
+ "account": unrealized_exchange_gain_loss_account,
+ "balance": get_balance_on(unrealized_exchange_gain_loss_account),
+ "debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
+ "credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
+ "exchange_rate": 1,
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
- })
- journal_entry_accounts.append({
- "account": d.get("account"),
- "party_type": d.get("party_type"),
- "party": d.get("party"),
- "account_currency": d.get("account_currency"),
- "balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
- reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
- "exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
- "reference_type": "Exchange Rate Revaluation",
- "reference_name": self.name
- })
-
- journal_entry_accounts.append({
- "account": unrealized_exchange_gain_loss_account,
- "balance": get_balance_on(unrealized_exchange_gain_loss_account),
- "debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
- "credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
- "exchange_rate": 1,
- "reference_type": "Exchange Rate Revaluation",
- "reference_name": self.name,
- })
+ }
+ )
journal_entry.set("accounts", journal_entry_accounts)
journal_entry.set_amounts_in_company_currency()
journal_entry.set_total_debit_credit()
return journal_entry.as_dict()
+
@frappe.whitelist()
def get_account_details(account, company, posting_date, party_type=None, party=None):
- account_currency, account_type = frappe.db.get_value("Account", account,
- ["account_currency", "account_type"])
+ account_currency, account_type = frappe.db.get_value(
+ "Account", account, ["account_currency", "account_type"]
+ )
if account_type in ["Receivable", "Payable"] and not (party_type and party):
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
account_details = {}
company_currency = erpnext.get_company_currency(company)
- balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False)
+ balance = get_balance_on(
+ account, date=posting_date, party_type=party_type, party=party, in_account_currency=False
+ )
if balance:
- balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party)
- current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0
+ balance_in_account_currency = get_balance_on(
+ account, date=posting_date, party_type=party_type, party=party
+ )
+ current_exchange_rate = (
+ balance / balance_in_account_currency if balance_in_account_currency else 0
+ )
new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
account_details = {
@@ -204,7 +248,7 @@ def get_account_details(account, company, posting_date, party_type=None, party=N
"balance_in_account_currency": balance_in_account_currency,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
- "new_balance_in_base_currency": new_balance_in_base_currency
+ "new_balance_in_base_currency": new_balance_in_base_currency,
}
return account_details
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py
index fe862507fb87..7eca9703c244 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py
@@ -1,9 +1,2 @@
def get_data():
- return {
- 'fieldname': 'reference_name',
- 'transactions': [
- {
- 'items': ['Journal Entry']
- }
- ]
- }
+ return {"fieldname": "reference_name", "transactions": [{"items": ["Journal Entry"]}]}
diff --git a/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py b/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
index 57b039d022b5..24e6c0c8721c 100644
--- a/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
+++ b/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
@@ -3,21 +3,11 @@
def get_data():
return {
- 'fieldname': 'finance_book',
- 'non_standard_fieldnames': {
- 'Asset': 'default_finance_book',
- 'Company': 'default_finance_book'
- },
- 'transactions': [
- {
- 'label': _('Assets'),
- 'items': ['Asset', 'Asset Value Adjustment']
- },
- {
- 'items': ['Company']
- },
- {
- 'items': ['Journal Entry']
- }
- ]
+ "fieldname": "finance_book",
+ "non_standard_fieldnames": {"Asset": "default_finance_book", "Company": "default_finance_book"},
+ "transactions": [
+ {"label": _("Assets"), "items": ["Asset", "Asset Value Adjustment"]},
+ {"items": ["Company"]},
+ {"items": ["Journal Entry"]},
+ ],
}
diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.py b/erpnext/accounts/doctype/finance_book/test_finance_book.py
index 5fb3d0a96cbf..7b2575d2c328 100644
--- a/erpnext/accounts/doctype/finance_book/test_finance_book.py
+++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py
@@ -13,30 +13,29 @@ def test_finance_book(self):
finance_book = create_finance_book()
# create jv entry
- jv = make_journal_entry("_Test Bank - _TC",
- "_Test Receivable - _TC", 100, save=False)
+ jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False)
- jv.accounts[1].update({
- "party_type": "Customer",
- "party": "_Test Customer"
- })
+ jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer"})
jv.finance_book = finance_book.finance_book_name
jv.submit()
# check the Finance Book in the GL Entry
- gl_entries = frappe.get_all("GL Entry", fields=["name", "finance_book"],
- filters={"voucher_type": "Journal Entry", "voucher_no": jv.name})
+ gl_entries = frappe.get_all(
+ "GL Entry",
+ fields=["name", "finance_book"],
+ filters={"voucher_type": "Journal Entry", "voucher_no": jv.name},
+ )
for gl_entry in gl_entries:
self.assertEqual(gl_entry.finance_book, finance_book.name)
+
def create_finance_book():
if not frappe.db.exists("Finance Book", "_Test Finance Book"):
- finance_book = frappe.get_doc({
- "doctype": "Finance Book",
- "finance_book_name": "_Test Finance Book"
- }).insert()
+ finance_book = frappe.get_doc(
+ {"doctype": "Finance Book", "finance_book_name": "_Test Finance Book"}
+ ).insert()
else:
finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index dd893f9fc80c..069ab5ea843c 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -9,7 +9,9 @@
from frappe.utils import add_days, add_years, cstr, getdate
-class FiscalYearIncorrectDate(frappe.ValidationError): pass
+class FiscalYearIncorrectDate(frappe.ValidationError):
+ pass
+
class FiscalYear(Document):
@frappe.whitelist()
@@ -22,19 +24,33 @@ def set_as_default(self):
# clear cache
frappe.clear_cache()
- msgprint(_("{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.").format(self.name))
+ msgprint(
+ _(
+ "{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect."
+ ).format(self.name)
+ )
def validate(self):
self.validate_dates()
self.validate_overlap()
if not self.is_new():
- year_start_end_dates = frappe.db.sql("""select year_start_date, year_end_date
- from `tabFiscal Year` where name=%s""", (self.name))
+ year_start_end_dates = frappe.db.sql(
+ """select year_start_date, year_end_date
+ from `tabFiscal Year` where name=%s""",
+ (self.name),
+ )
if year_start_end_dates:
- if getdate(self.year_start_date) != year_start_end_dates[0][0] or getdate(self.year_end_date) != year_start_end_dates[0][1]:
- frappe.throw(_("Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."))
+ if (
+ getdate(self.year_start_date) != year_start_end_dates[0][0]
+ or getdate(self.year_end_date) != year_start_end_dates[0][1]
+ ):
+ frappe.throw(
+ _(
+ "Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."
+ )
+ )
def validate_dates(self):
if self.is_short_year:
@@ -43,14 +59,18 @@ def validate_dates(self):
return
if getdate(self.year_start_date) > getdate(self.year_end_date):
- frappe.throw(_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
- FiscalYearIncorrectDate)
+ frappe.throw(
+ _("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
+ FiscalYearIncorrectDate,
+ )
date = getdate(self.year_start_date) + relativedelta(years=1) - relativedelta(days=1)
if getdate(self.year_end_date) != date:
- frappe.throw(_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
- FiscalYearIncorrectDate)
+ frappe.throw(
+ _("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
+ FiscalYearIncorrectDate,
+ )
def on_update(self):
check_duplicate_fiscal_year(self)
@@ -59,11 +79,16 @@ def on_update(self):
def on_trash(self):
global_defaults = frappe.get_doc("Global Defaults")
if global_defaults.current_fiscal_year == self.name:
- frappe.throw(_("You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings").format(self.name))
+ frappe.throw(
+ _(
+ "You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings"
+ ).format(self.name)
+ )
frappe.cache().delete_value("fiscal_years")
def validate_overlap(self):
- existing_fiscal_years = frappe.db.sql("""select name from `tabFiscal Year`
+ existing_fiscal_years = frappe.db.sql(
+ """select name from `tabFiscal Year`
where (
(%(year_start_date)s between year_start_date and year_end_date)
or (%(year_end_date)s between year_start_date and year_end_date)
@@ -73,13 +98,18 @@ def validate_overlap(self):
{
"year_start_date": self.year_start_date,
"year_end_date": self.year_end_date,
- "name": self.name or "No Name"
- }, as_dict=True)
+ "name": self.name or "No Name",
+ },
+ as_dict=True,
+ )
if existing_fiscal_years:
for existing in existing_fiscal_years:
- company_for_existing = frappe.db.sql_list("""select company from `tabFiscal Year Company`
- where parent=%s""", existing.name)
+ company_for_existing = frappe.db.sql_list(
+ """select company from `tabFiscal Year Company`
+ where parent=%s""",
+ existing.name,
+ )
overlap = False
if not self.get("companies") or not company_for_existing:
@@ -90,20 +120,36 @@ def validate_overlap(self):
overlap = True
if overlap:
- frappe.throw(_("Year start date or end date is overlapping with {0}. To avoid please set company")
- .format(existing.name), frappe.NameError)
+ frappe.throw(
+ _("Year start date or end date is overlapping with {0}. To avoid please set company").format(
+ existing.name
+ ),
+ frappe.NameError,
+ )
+
@frappe.whitelist()
def check_duplicate_fiscal_year(doc):
- year_start_end_dates = frappe.db.sql("""select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""", (doc.name))
+ year_start_end_dates = frappe.db.sql(
+ """select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""",
+ (doc.name),
+ )
for fiscal_year, ysd, yed in year_start_end_dates:
- if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (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))
+ if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (
+ 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
+ )
+ )
@frappe.whitelist()
def auto_create_fiscal_year():
- for d in frappe.db.sql("""select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""):
+ for d in frappe.db.sql(
+ """select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""
+ ):
try:
current_fy = frappe.get_doc("Fiscal Year", d[0])
@@ -114,16 +160,14 @@ def auto_create_fiscal_year():
start_year = cstr(new_fy.year_start_date.year)
end_year = cstr(new_fy.year_end_date.year)
- new_fy.year = start_year if start_year==end_year else (start_year + "-" + end_year)
+ new_fy.year = start_year if start_year == end_year else (start_year + "-" + end_year)
new_fy.auto_created = 1
new_fy.insert(ignore_permissions=True)
except frappe.NameError:
pass
+
def get_from_and_to_date(fiscal_year):
- fields = [
- "year_start_date as from_date",
- "year_end_date as to_date"
- ]
+ fields = ["year_start_date as from_date", "year_end_date as to_date"]
return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1)
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
index 892a2c62cff0..bc966916ef30 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
@@ -3,19 +3,13 @@
def get_data():
return {
- 'fieldname': 'fiscal_year',
- 'transactions': [
+ "fieldname": "fiscal_year",
+ "transactions": [
+ {"label": _("Budgets"), "items": ["Budget"]},
+ {"label": _("References"), "items": ["Period Closing Voucher"]},
{
- 'label': _('Budgets'),
- 'items': ['Budget']
+ "label": _("Target Details"),
+ "items": ["Sales Person", "Sales Partner", "Territory", "Monthly Distribution"],
},
- {
- 'label': _('References'),
- 'items': ['Period Closing Voucher']
- },
- {
- 'label': _('Target Details'),
- 'items': ['Sales Person', 'Sales Partner', 'Territory', 'Monthly Distribution']
- }
- ]
+ ],
}
diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
index 69e13a407de8..6e946f746606 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -11,43 +11,48 @@
test_ignore = ["Company"]
-class TestFiscalYear(unittest.TestCase):
+class TestFiscalYear(unittest.TestCase):
def test_extra_year(self):
if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"):
frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000")
- fy = frappe.get_doc({
- "doctype": "Fiscal Year",
- "year": "_Test Fiscal Year 2000",
- "year_end_date": "2002-12-31",
- "year_start_date": "2000-04-01"
- })
+ fy = frappe.get_doc(
+ {
+ "doctype": "Fiscal Year",
+ "year": "_Test Fiscal Year 2000",
+ "year_end_date": "2002-12-31",
+ "year_start_date": "2000-04-01",
+ }
+ )
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
def test_record_generator():
test_records = [
- {
- "doctype": "Fiscal Year",
- "year": "_Test Short Fiscal Year 2011",
- "is_short_year": 1,
- "year_end_date": "2011-04-01",
- "year_start_date": "2011-12-31"
- }
+ {
+ "doctype": "Fiscal Year",
+ "year": "_Test Short Fiscal Year 2011",
+ "is_short_year": 1,
+ "year_end_date": "2011-04-01",
+ "year_start_date": "2011-12-31",
+ }
]
start = 2012
end = now_datetime().year + 5
for year in range(start, end):
- test_records.append({
- "doctype": "Fiscal Year",
- "year": f"_Test Fiscal Year {year}",
- "year_start_date": f"{year}-01-01",
- "year_end_date": f"{year}-12-31"
- })
+ test_records.append(
+ {
+ "doctype": "Fiscal Year",
+ "year": f"_Test Fiscal Year {year}",
+ "year_start_date": f"{year}-01-01",
+ "year_end_date": f"{year}-12-31",
+ }
+ )
return test_records
+
test_records = test_record_generator()
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 9d1452b1b383..aee7f0e0f9e0 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -25,6 +25,8 @@
)
exclude_from_linked_with = True
+
+
class GLEntry(Document):
def autoname(self):
"""
@@ -32,6 +34,8 @@ def autoname(self):
name will be changed using autoname options (in a scheduled job)
"""
self.name = frappe.generate_hash(txt="", length=10)
+ if self.meta.autoname == "hash":
+ self.to_rename = 0
def validate(self):
self.flags.ignore_submit_comment = True
@@ -55,14 +59,18 @@ def on_update(self):
validate_frozen_account(self.account, adv_adj)
# Update outstanding amt on against voucher
- if (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)
+ if (
+ 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
+ )
def check_mandatory(self):
- mandatory = ['account','voucher_type','voucher_no','company']
+ mandatory = ["account", "voucher_type", "voucher_no", "company"]
for k in mandatory:
if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
@@ -70,29 +78,40 @@ def check_mandatory(self):
if not (self.party_type and self.party):
account_type = frappe.get_cached_value("Account", self.account, "account_type")
if account_type == "Receivable":
- frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
- .format(self.voucher_type, self.voucher_no, self.account))
+ frappe.throw(
+ _("{0} {1}: Customer is required against Receivable account {2}").format(
+ self.voucher_type, self.voucher_no, self.account
+ )
+ )
elif account_type == "Payable":
- frappe.throw(_("{0} {1}: Supplier is required against Payable account {2}")
- .format(self.voucher_type, self.voucher_no, self.account))
+ frappe.throw(
+ _("{0} {1}: Supplier is required against Payable account {2}").format(
+ self.voucher_type, self.voucher_no, self.account
+ )
+ )
# Zero value transaction is not allowed
if not (flt(self.debit, self.precision("debit")) or flt(self.credit, self.precision("credit"))):
- frappe.throw(_("{0} {1}: Either debit or credit amount is required for {2}")
- .format(self.voucher_type, self.voucher_no, self.account))
+ frappe.throw(
+ _("{0} {1}: Either debit or credit amount is required for {2}").format(
+ self.voucher_type, self.voucher_no, self.account
+ )
+ )
def pl_must_have_cost_center(self):
"""Validate that profit and loss type account GL entries have a cost center."""
- if self.cost_center or self.voucher_type == 'Period Closing Voucher':
+ if self.cost_center or self.voucher_type == "Period Closing Voucher":
return
if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
- self.voucher_type, self.voucher_no, self.account)
+ self.voucher_type, self.voucher_no, self.account
+ )
msg += " "
- msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
- self.voucher_type)
+ msg += _(
+ "Please set the cost center field in {0} or setup a default Cost Center for the Company."
+ ).format(self.voucher_type)
frappe.throw(msg, title=_("Missing Cost Center"))
@@ -100,17 +119,31 @@ def validate_dimensions_for_pl_and_bs(self):
account_type = frappe.db.get_value("Account", self.account, "report_type")
for dimension in get_checks_for_pl_and_bs_accounts():
- if account_type == "Profit and Loss" \
- and self.company == dimension.company and dimension.mandatory_for_pl and not dimension.disabled:
+ if (
+ account_type == "Profit and Loss"
+ and self.company == dimension.company
+ and dimension.mandatory_for_pl
+ and not dimension.disabled
+ ):
if not self.get(dimension.fieldname):
- frappe.throw(_("Accounting Dimension {0} is required for 'Profit and Loss' account {1}.")
- .format(dimension.label, self.account))
-
- if account_type == "Balance Sheet" \
- and self.company == dimension.company and dimension.mandatory_for_bs and not dimension.disabled:
+ frappe.throw(
+ _("Accounting Dimension {0} is required for 'Profit and Loss' account {1}.").format(
+ dimension.label, self.account
+ )
+ )
+
+ if (
+ account_type == "Balance Sheet"
+ and self.company == dimension.company
+ and dimension.mandatory_for_bs
+ and not dimension.disabled
+ ):
if not self.get(dimension.fieldname):
- frappe.throw(_("Accounting Dimension {0} is required for 'Balance Sheet' account {1}.")
- .format(dimension.label, self.account))
+ frappe.throw(
+ _("Accounting Dimension {0} is required for 'Balance Sheet' account {1}.").format(
+ dimension.label, self.account
+ )
+ )
def validate_allowed_dimensions(self):
dimension_filter_map = get_dimension_filter_map()
@@ -119,56 +152,97 @@ def validate_allowed_dimensions(self):
account = key[1]
if self.account == account:
- if value['is_mandatory'] and not self.get(dimension):
- frappe.throw(_("{0} is mandatory for account {1}").format(
- frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryAccountDimensionError)
-
- if value['allow_or_restrict'] == 'Allow':
- if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']:
- frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
- frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
+ if value["is_mandatory"] and not self.get(dimension):
+ frappe.throw(
+ _("{0} is mandatory for account {1}").format(
+ frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
+ ),
+ MandatoryAccountDimensionError,
+ )
+
+ if value["allow_or_restrict"] == "Allow":
+ if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
+ frappe.throw(
+ _("Invalid value {0} for {1} against account {2}").format(
+ frappe.bold(self.get(dimension)),
+ frappe.bold(frappe.unscrub(dimension)),
+ frappe.bold(self.account),
+ ),
+ InvalidAccountDimensionError,
+ )
else:
- if self.get(dimension) and self.get(dimension) in value['allowed_dimensions']:
- frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
- frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
+ if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
+ frappe.throw(
+ _("Invalid value {0} for {1} against account {2}").format(
+ frappe.bold(self.get(dimension)),
+ frappe.bold(frappe.unscrub(dimension)),
+ frappe.bold(self.account),
+ ),
+ InvalidAccountDimensionError,
+ )
def check_pl_account(self):
- if self.is_opening=='Yes' and \
- frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss":
- frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry")
- .format(self.voucher_type, self.voucher_no, self.account))
+ if (
+ self.is_opening == "Yes"
+ and frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss"
+ and not self.is_cancelled
+ ):
+ frappe.throw(
+ _("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry").format(
+ self.voucher_type, self.voucher_no, self.account
+ )
+ )
def validate_account_details(self, adv_adj):
"""Account must be ledger, active and not freezed"""
- ret = frappe.db.sql("""select is_group, docstatus, company
- from tabAccount where name=%s""", self.account, as_dict=1)[0]
-
- if ret.is_group==1:
- frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions''')
- .format(self.voucher_type, self.voucher_no, self.account))
-
- if ret.docstatus==2:
- frappe.throw(_("{0} {1}: Account {2} is inactive")
- .format(self.voucher_type, self.voucher_no, self.account))
+ ret = frappe.db.sql(
+ """select is_group, docstatus, company
+ from tabAccount where name=%s""",
+ self.account,
+ as_dict=1,
+ )[0]
+
+ if ret.is_group == 1:
+ frappe.throw(
+ _(
+ """{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions"""
+ ).format(self.voucher_type, self.voucher_no, self.account)
+ )
+
+ if ret.docstatus == 2:
+ frappe.throw(
+ _("{0} {1}: Account {2} is inactive").format(self.voucher_type, self.voucher_no, self.account)
+ )
if ret.company != self.company:
- frappe.throw(_("{0} {1}: Account {2} does not belong to Company {3}")
- .format(self.voucher_type, self.voucher_no, self.account, self.company))
+ frappe.throw(
+ _("{0} {1}: Account {2} does not belong to Company {3}").format(
+ self.voucher_type, self.voucher_no, self.account, self.company
+ )
+ )
def validate_cost_center(self):
- if not self.cost_center: return
+ 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(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
- .format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
-
- if (self.voucher_type != 'Period Closing Voucher' and is_group):
- frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
- self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
+ frappe.throw(
+ _("{0} {1}: Cost Center {2} does not belong to Company {3}").format(
+ self.voucher_type, self.voucher_no, self.cost_center, self.company
+ )
+ )
+
+ if self.voucher_type != "Period Closing Voucher" and is_group:
+ frappe.throw(
+ _(
+ """{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions"""
+ ).format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))
+ )
def validate_party(self):
validate_party_frozen_disabled(self.party_type, self.party)
@@ -181,9 +255,12 @@ def validate_currency(self):
self.account_currency = account_currency or company_currency
if account_currency != self.account_currency:
- frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")
- .format(self.voucher_type, self.voucher_no, self.account,
- (account_currency or company_currency)), InvalidAccountCurrency)
+ frappe.throw(
+ _("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}").format(
+ self.voucher_type, self.voucher_no, self.account, (account_currency or company_currency)
+ ),
+ InvalidAccountCurrency,
+ )
if self.party_type and self.party:
validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
@@ -192,51 +269,80 @@ def validate_and_set_fiscal_year(self):
if not self.fiscal_year:
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
+
def validate_balance_type(account, adv_adj=False):
if not adv_adj and account:
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
if balance_must_be:
- balance = frappe.db.sql("""select sum(debit) - sum(credit)
- from `tabGL Entry` where account = %s""", account)[0][0]
-
- if (balance_must_be=="Debit" and flt(balance) < 0) or \
- (balance_must_be=="Credit" and flt(balance) > 0):
- frappe.throw(_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be)))
-
-def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
+ balance = frappe.db.sql(
+ """select sum(debit) - sum(credit)
+ from `tabGL Entry` where account = %s""",
+ account,
+ )[0][0]
+
+ if (balance_must_be == "Debit" and flt(balance) < 0) or (
+ balance_must_be == "Credit" and flt(balance) > 0
+ ):
+ frappe.throw(
+ _("Balance for Account {0} must always be {1}").format(account, _(balance_must_be))
+ )
+
+
+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(frappe.db.escape(party_type), frappe.db.escape(party))
+ party_condition = " and party_type={0} and party={1}".format(
+ frappe.db.escape(party_type), frappe.db.escape(party)
+ )
else:
party_condition = ""
if against_voucher_type == "Sales Invoice":
party_account = frappe.db.get_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 = "and account in ({0}, {1})".format(
+ frappe.db.escape(account), frappe.db.escape(party_account)
+ )
else:
account_condition = " and account = {0}".format(frappe.db.escape(account))
# get final outstanding amt
- bal = flt(frappe.db.sql("""
+ bal = flt(
+ frappe.db.sql(
+ """
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),
- (against_voucher_type, against_voucher))[0][0] or 0.0)
-
- if against_voucher_type == 'Purchase Invoice':
+ {0} {1}""".format(
+ party_condition, account_condition
+ ),
+ (against_voucher_type, against_voucher),
+ )[0][0]
+ or 0.0
+ )
+
+ if against_voucher_type == "Purchase Invoice":
bal = -bal
elif against_voucher_type == "Journal Entry":
- against_voucher_amount = flt(frappe.db.sql("""
+ against_voucher_amount = flt(
+ frappe.db.sql(
+ """
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), (against_voucher, account))[0][0])
+ and account = %s and (against_voucher is null or against_voucher='') {0}""".format(
+ party_condition
+ ),
+ (against_voucher, account),
+ )[0][0]
+ )
if not against_voucher_amount:
- frappe.throw(_("Against Journal Entry {0} is already adjusted against some other voucher")
- .format(against_voucher))
+ frappe.throw(
+ _("Against Journal Entry {0} is already adjusted against some other voucher").format(
+ against_voucher
+ )
+ )
bal = against_voucher_amount + bal
if against_voucher_amount < 0:
@@ -244,44 +350,51 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
# 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)))
+ frappe.throw(
+ _("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"]:
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
# Didn't use db_set for optimisation purpose
ref_doc.outstanding_amount = bal
- frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal)
+ frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)
ref_doc.set_status(update=True)
def validate_frozen_account(account, adv_adj=None):
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
- if frozen_account == 'Yes' and not adv_adj:
- frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
- 'frozen_accounts_modifier')
+ if frozen_account == "Yes" and not adv_adj:
+ frozen_accounts_modifier = frappe.db.get_value(
+ "Accounts Settings", None, "frozen_accounts_modifier"
+ )
if not frozen_accounts_modifier:
frappe.throw(_("Account {0} is frozen").format(account))
elif frozen_accounts_modifier not in frappe.get_roles():
frappe.throw(_("Not authorized to edit frozen Account {0}").format(account))
+
def update_against_account(voucher_type, voucher_no):
- entries = frappe.db.get_all("GL Entry",
+ entries = frappe.db.get_all(
+ "GL Entry",
filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
- fields=["name", "party", "against", "debit", "credit", "account", "company"])
+ fields=["name", "party", "against", "debit", "credit", "account", "company"],
+ )
if not entries:
return
company_currency = erpnext.get_company_currency(entries[0].company)
- precision = get_field_precision(frappe.get_meta("GL Entry")
- .get_field("debit"), company_currency)
+ precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
accounts_debited, accounts_credited = [], []
for d in entries:
- if flt(d.debit, precision) > 0: accounts_debited.append(d.party or d.account)
- if flt(d.credit, precision) > 0: accounts_credited.append(d.party or d.account)
+ if flt(d.debit, precision) > 0:
+ accounts_debited.append(d.party or d.account)
+ if flt(d.credit, precision) > 0:
+ accounts_credited.append(d.party or d.account)
for d in entries:
if flt(d.debit, precision) > 0:
@@ -292,14 +405,17 @@ def update_against_account(voucher_type, voucher_no):
if d.against != new_against:
frappe.db.set_value("GL Entry", d.name, "against", new_against)
+
def on_doctype_update():
frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
+
def rename_gle_sle_docs():
for doctype in ["GL Entry", "Stock Ledger Entry"]:
rename_temporarily_named_docs(doctype)
+
def rename_temporarily_named_docs(doctype):
"""Rename temporarily named docs using autoname options"""
docs_to_rename = frappe.get_all(doctype, {"to_rename": "1"}, order_by="creation", limit=50000)
@@ -310,5 +426,5 @@ def rename_temporarily_named_docs(doctype):
frappe.db.sql(
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
(newname, oldname),
- auto_commit=True
+ auto_commit=True,
)
diff --git a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
index 3de239468920..b188b09843ad 100644
--- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
@@ -14,48 +14,68 @@
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",
- "_Test Bank - _TC", 100, "_Test Cost Center - _TC", submit=False)
+ jv = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC",
+ 100,
+ "_Test Cost Center - _TC",
+ submit=False,
+ )
jv.get("accounts")[0].debit = 100.01
jv.flags.ignore_validate = True
jv.submit()
- round_off_entry = frappe.db.sql("""select name from `tabGL Entry`
+ round_off_entry = frappe.db.sql(
+ """select name from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_no = %s
and account='_Test Write Off - _TC' and cost_center='_Test Cost Center - _TC'
- and debit = 0 and credit = '.01'""", jv.name)
+ and debit = 0 and credit = '.01'""",
+ jv.name,
+ )
self.assertTrue(round_off_entry)
def test_rename_entries(self):
- je = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True)
+ je = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True
+ )
rename_gle_sle_docs()
naming_series = parse_naming_series(parts=frappe.get_meta("GL Entry").autoname.split(".")[:-1])
- je = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True)
+ je = make_journal_entry(
+ "_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True
+ )
- gl_entries = frappe.get_all("GL Entry",
+ gl_entries = frappe.get_all(
+ "GL Entry",
fields=["name", "to_rename"],
filters={"voucher_type": "Journal Entry", "voucher_no": je.name},
- order_by="creation"
+ order_by="creation",
)
self.assertTrue(all(entry.to_rename == 1 for entry in gl_entries))
- old_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]
+ old_naming_series_current_value = frappe.db.sql(
+ "SELECT current from tabSeries where name = %s", naming_series
+ )[0][0]
rename_gle_sle_docs()
- new_gl_entries = frappe.get_all("GL Entry",
+ new_gl_entries = frappe.get_all(
+ "GL Entry",
fields=["name", "to_rename"],
filters={"voucher_type": "Journal Entry", "voucher_no": je.name},
- order_by="creation"
+ order_by="creation",
)
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)))
- new_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]
+ new_naming_series_current_value = frappe.db.sql(
+ "SELECT current from tabSeries where name = %s", naming_series
+ )[0][0]
self.assertEqual(old_naming_series_current_value + 2, new_naming_series_current_value)
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 09c389de7387..5bd4585a9a8d 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -33,19 +33,32 @@ def validate_mandatory(self):
frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
def validate_invoices(self):
- discounted_invoices = [record.sales_invoice for record in
- frappe.get_all("Discounted Invoice",fields=["sales_invoice"], filters={"docstatus":1})]
+ discounted_invoices = [
+ record.sales_invoice
+ for record in frappe.get_all(
+ "Discounted Invoice", fields=["sales_invoice"], filters={"docstatus": 1}
+ )
+ ]
for record in self.invoices:
if record.sales_invoice in discounted_invoices:
- frappe.throw(_("Row({0}): {1} is already discounted in {2}")
- .format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)))
-
- actual_outstanding = frappe.db.get_value("Sales Invoice", record.sales_invoice,"outstanding_amount")
- if record.outstanding_amount > actual_outstanding :
- 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)))
+ frappe.throw(
+ _("Row({0}): {1} is already discounted in {2}").format(
+ record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)
+ )
+ )
+
+ actual_outstanding = frappe.db.get_value(
+ "Sales Invoice", record.sales_invoice, "outstanding_amount"
+ )
+ if record.outstanding_amount > actual_outstanding:
+ 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)
+ )
+ )
def calculate_total_amount(self):
self.total_amount = sum(flt(d.outstanding_amount) for d in self.invoices)
@@ -73,24 +86,21 @@ def set_status(self, status=None, cancel=0):
self.status = "Cancelled"
if cancel:
- self.db_set('status', self.status, update_modified = True)
+ self.db_set("status", self.status, update_modified=True)
def update_sales_invoice(self):
for d in self.invoices:
if self.docstatus == 1:
is_discounted = 1
else:
- discounted_invoice = frappe.db.exists({
- "doctype": "Discounted Invoice",
- "sales_invoice": d.sales_invoice,
- "docstatus": 1
- })
+ discounted_invoice = frappe.db.exists(
+ {"doctype": "Discounted Invoice", "sales_invoice": d.sales_invoice, "docstatus": 1}
+ )
is_discounted = 1 if discounted_invoice else 0
frappe.db.set_value("Sales Invoice", d.sales_invoice, "is_discounted", is_discounted)
def make_gl_entries(self):
- company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
-
+ company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
gl_entries = []
invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
@@ -102,135 +112,182 @@ def make_gl_entries(self):
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
if d.outstanding_amount:
- outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
- d.precision("outstanding_amount"))
- ar_credit_account_currency = frappe.get_cached_value("Account", self.accounts_receivable_credit, "currency")
-
- gl_entries.append(self.get_gl_dict({
- "account": inv.debit_to,
- "party_type": "Customer",
- "party": d.customer,
- "against": self.accounts_receivable_credit,
- "credit": outstanding_in_company_currency,
- "credit_in_account_currency": outstanding_in_company_currency \
- if inv.party_account_currency==company_currency else d.outstanding_amount,
- "cost_center": inv.cost_center,
- "against_voucher": d.sales_invoice,
- "against_voucher_type": "Sales Invoice"
- }, inv.party_account_currency, item=inv))
-
- gl_entries.append(self.get_gl_dict({
- "account": self.accounts_receivable_credit,
- "party_type": "Customer",
- "party": d.customer,
- "against": inv.debit_to,
- "debit": outstanding_in_company_currency,
- "debit_in_account_currency": outstanding_in_company_currency \
- if ar_credit_account_currency==company_currency else d.outstanding_amount,
- "cost_center": inv.cost_center,
- "against_voucher": d.sales_invoice,
- "against_voucher_type": "Sales Invoice"
- }, ar_credit_account_currency, item=inv))
-
- make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
+ outstanding_in_company_currency = flt(
+ d.outstanding_amount * inv.conversion_rate, d.precision("outstanding_amount")
+ )
+ ar_credit_account_currency = frappe.get_cached_value(
+ "Account", self.accounts_receivable_credit, "currency"
+ )
+
+ gl_entries.append(
+ self.get_gl_dict(
+ {
+ "account": inv.debit_to,
+ "party_type": "Customer",
+ "party": d.customer,
+ "against": self.accounts_receivable_credit,
+ "credit": outstanding_in_company_currency,
+ "credit_in_account_currency": outstanding_in_company_currency
+ if inv.party_account_currency == company_currency
+ else d.outstanding_amount,
+ "cost_center": inv.cost_center,
+ "against_voucher": d.sales_invoice,
+ "against_voucher_type": "Sales Invoice",
+ },
+ inv.party_account_currency,
+ item=inv,
+ )
+ )
+
+ gl_entries.append(
+ self.get_gl_dict(
+ {
+ "account": self.accounts_receivable_credit,
+ "party_type": "Customer",
+ "party": d.customer,
+ "against": inv.debit_to,
+ "debit": outstanding_in_company_currency,
+ "debit_in_account_currency": outstanding_in_company_currency
+ if ar_credit_account_currency == company_currency
+ else d.outstanding_amount,
+ "cost_center": inv.cost_center,
+ "against_voucher": d.sales_invoice,
+ "against_voucher_type": "Sales Invoice",
+ },
+ ar_credit_account_currency,
+ item=inv,
+ )
+ )
+
+ make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No")
@frappe.whitelist()
def create_disbursement_entry(self):
je = frappe.new_doc("Journal Entry")
- je.voucher_type = 'Journal Entry'
+ je.voucher_type = "Journal Entry"
je.company = self.company
- je.remark = 'Loan Disbursement entry against Invoice Discounting: ' + self.name
+ je.remark = "Loan Disbursement entry against Invoice Discounting: " + self.name
- je.append("accounts", {
- "account": self.bank_account,
- "debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
- "cost_center": erpnext.get_default_cost_center(self.company)
- })
+ je.append(
+ "accounts",
+ {
+ "account": self.bank_account,
+ "debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ },
+ )
if self.bank_charges:
- je.append("accounts", {
- "account": self.bank_charges_account,
- "debit_in_account_currency": flt(self.bank_charges),
- "cost_center": erpnext.get_default_cost_center(self.company)
- })
-
- je.append("accounts", {
- "account": self.short_term_loan,
- "credit_in_account_currency": flt(self.total_amount),
- "cost_center": erpnext.get_default_cost_center(self.company),
- "reference_type": "Invoice Discounting",
- "reference_name": self.name
- })
- for d in self.invoices:
- je.append("accounts", {
- "account": self.accounts_receivable_discounted,
- "debit_in_account_currency": flt(d.outstanding_amount),
+ je.append(
+ "accounts",
+ {
+ "account": self.bank_charges_account,
+ "debit_in_account_currency": flt(self.bank_charges),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ },
+ )
+
+ je.append(
+ "accounts",
+ {
+ "account": self.short_term_loan,
+ "credit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
- "party_type": "Customer",
- "party": d.customer
- })
+ },
+ )
+ for d in self.invoices:
+ je.append(
+ "accounts",
+ {
+ "account": self.accounts_receivable_discounted,
+ "debit_in_account_currency": flt(d.outstanding_amount),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ "reference_type": "Invoice Discounting",
+ "reference_name": self.name,
+ "party_type": "Customer",
+ "party": d.customer,
+ },
+ )
- je.append("accounts", {
- "account": self.accounts_receivable_credit,
- "credit_in_account_currency": flt(d.outstanding_amount),
- "cost_center": erpnext.get_default_cost_center(self.company),
- "reference_type": "Invoice Discounting",
- "reference_name": self.name,
- "party_type": "Customer",
- "party": d.customer
- })
+ je.append(
+ "accounts",
+ {
+ "account": self.accounts_receivable_credit,
+ "credit_in_account_currency": flt(d.outstanding_amount),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ "reference_type": "Invoice Discounting",
+ "reference_name": self.name,
+ "party_type": "Customer",
+ "party": d.customer,
+ },
+ )
return je
@frappe.whitelist()
def close_loan(self):
je = frappe.new_doc("Journal Entry")
- je.voucher_type = 'Journal Entry'
+ je.voucher_type = "Journal Entry"
je.company = self.company
- je.remark = 'Loan Settlement entry against Invoice Discounting: ' + self.name
-
- je.append("accounts", {
- "account": self.short_term_loan,
- "debit_in_account_currency": flt(self.total_amount),
- "cost_center": erpnext.get_default_cost_center(self.company),
- "reference_type": "Invoice Discounting",
- "reference_name": self.name,
- })
-
- je.append("accounts", {
- "account": self.bank_account,
- "credit_in_account_currency": flt(self.total_amount),
- "cost_center": erpnext.get_default_cost_center(self.company)
- })
+ je.remark = "Loan Settlement entry against Invoice Discounting: " + self.name
+
+ je.append(
+ "accounts",
+ {
+ "account": self.short_term_loan,
+ "debit_in_account_currency": flt(self.total_amount),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ "reference_type": "Invoice Discounting",
+ "reference_name": self.name,
+ },
+ )
+
+ je.append(
+ "accounts",
+ {
+ "account": self.bank_account,
+ "credit_in_account_currency": flt(self.total_amount),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ },
+ )
if getdate(self.loan_end_date) > getdate(nowdate()):
for d in self.invoices:
- outstanding_amount = frappe.db.get_value("Sales Invoice", d.sales_invoice, "outstanding_amount")
+ outstanding_amount = frappe.db.get_value(
+ "Sales Invoice", d.sales_invoice, "outstanding_amount"
+ )
if flt(outstanding_amount) > 0:
- je.append("accounts", {
- "account": self.accounts_receivable_discounted,
- "credit_in_account_currency": flt(outstanding_amount),
- "cost_center": erpnext.get_default_cost_center(self.company),
- "reference_type": "Invoice Discounting",
- "reference_name": self.name,
- "party_type": "Customer",
- "party": d.customer
- })
-
- je.append("accounts", {
- "account": self.accounts_receivable_unpaid,
- "debit_in_account_currency": flt(outstanding_amount),
- "cost_center": erpnext.get_default_cost_center(self.company),
- "reference_type": "Invoice Discounting",
- "reference_name": self.name,
- "party_type": "Customer",
- "party": d.customer
- })
+ je.append(
+ "accounts",
+ {
+ "account": self.accounts_receivable_discounted,
+ "credit_in_account_currency": flt(outstanding_amount),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ "reference_type": "Invoice Discounting",
+ "reference_name": self.name,
+ "party_type": "Customer",
+ "party": d.customer,
+ },
+ )
+
+ je.append(
+ "accounts",
+ {
+ "account": self.accounts_receivable_unpaid,
+ "debit_in_account_currency": flt(outstanding_amount),
+ "cost_center": erpnext.get_default_cost_center(self.company),
+ "reference_type": "Invoice Discounting",
+ "reference_name": self.name,
+ "party_type": "Customer",
+ "party": d.customer,
+ },
+ )
return je
+
@frappe.whitelist()
def get_invoices(filters):
filters = frappe._dict(json.loads(filters))
@@ -250,7 +307,8 @@ def get_invoices(filters):
if cond:
where_condition += " and " + " and ".join(cond)
- return frappe.db.sql("""
+ return frappe.db.sql(
+ """
select
name as sales_invoice,
customer,
@@ -264,17 +322,26 @@ def get_invoices(filters):
%s
and not exists(select di.name from `tabDiscounted Invoice` di
where di.docstatus=1 and di.sales_invoice=si.name)
- """ % where_condition, filters, as_dict=1)
+ """
+ % where_condition,
+ filters,
+ as_dict=1,
+ )
+
def get_party_account_based_on_invoice_discounting(sales_invoice):
party_account = None
- invoice_discounting = frappe.db.sql("""
+ invoice_discounting = frappe.db.sql(
+ """
select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
where par.name=ch.parent
and par.docstatus=1
and ch.sales_invoice = %s
- """, (sales_invoice), as_dict=1)
+ """,
+ (sales_invoice),
+ as_dict=1,
+ )
if invoice_discounting:
if invoice_discounting[0].status == "Disbursed":
party_account = invoice_discounting[0].accounts_receivable_discounted
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
index b7484293023c..a442231d9a40 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
@@ -3,18 +3,10 @@
def get_data():
return {
- 'fieldname': 'reference_name',
- 'internal_links': {
- 'Sales Invoice': ['invoices', 'sales_invoice']
- },
- 'transactions': [
- {
- 'label': _('Reference'),
- 'items': ['Sales Invoice']
- },
- {
- 'label': _('Payment'),
- 'items': ['Payment Entry', 'Journal Entry']
- }
- ]
+ "fieldname": "reference_name",
+ "internal_links": {"Sales Invoice": ["invoices", "sales_invoice"]},
+ "transactions": [
+ {"label": _("Reference"), "items": ["Sales Invoice"]},
+ {"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]},
+ ],
}
diff --git a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
index d1d4be36f17b..a85fdfcad7f7 100644
--- a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
@@ -14,52 +14,74 @@
class TestInvoiceDiscounting(unittest.TestCase):
def setUp(self):
- self.ar_credit = create_account(account_name="_Test Accounts Receivable Credit", parent_account = "Accounts Receivable - _TC", company="_Test Company")
- self.ar_discounted = create_account(account_name="_Test Accounts Receivable Discounted", parent_account = "Accounts Receivable - _TC", company="_Test Company")
- self.ar_unpaid = create_account(account_name="_Test Accounts Receivable Unpaid", parent_account = "Accounts Receivable - _TC", company="_Test Company")
- self.short_term_loan = create_account(account_name="_Test Short Term Loan", parent_account = "Source of Funds (Liabilities) - _TC", company="_Test Company")
- self.bank_account = create_account(account_name="_Test Bank 2", parent_account = "Bank Accounts - _TC", company="_Test Company")
- self.bank_charges_account = create_account(account_name="_Test Bank Charges Account", parent_account = "Expenses - _TC", company="_Test Company")
+ self.ar_credit = create_account(
+ account_name="_Test Accounts Receivable Credit",
+ parent_account="Accounts Receivable - _TC",
+ company="_Test Company",
+ )
+ self.ar_discounted = create_account(
+ account_name="_Test Accounts Receivable Discounted",
+ parent_account="Accounts Receivable - _TC",
+ company="_Test Company",
+ )
+ self.ar_unpaid = create_account(
+ account_name="_Test Accounts Receivable Unpaid",
+ parent_account="Accounts Receivable - _TC",
+ company="_Test Company",
+ )
+ self.short_term_loan = create_account(
+ account_name="_Test Short Term Loan",
+ parent_account="Source of Funds (Liabilities) - _TC",
+ company="_Test Company",
+ )
+ self.bank_account = create_account(
+ account_name="_Test Bank 2", parent_account="Bank Accounts - _TC", company="_Test Company"
+ )
+ self.bank_charges_account = create_account(
+ account_name="_Test Bank Charges Account",
+ parent_account="Expenses - _TC",
+ company="_Test Company",
+ )
frappe.db.set_value("Company", "_Test Company", "default_bank_account", self.bank_account)
def test_total_amount(self):
inv1 = create_sales_invoice(rate=200)
inv2 = create_sales_invoice(rate=500)
- inv_disc = create_invoice_discounting([inv1.name, inv2.name],
+ inv_disc = create_invoice_discounting(
+ [inv1.name, inv2.name],
do_not_submit=True,
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
- bank_account=self.bank_account
- )
+ bank_account=self.bank_account,
+ )
self.assertEqual(inv_disc.total_amount, 700)
def test_gl_entries_in_base_currency(self):
inv = create_sales_invoice(rate=200)
- inv_disc = create_invoice_discounting([inv.name],
+ inv_disc = create_invoice_discounting(
+ [inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
- bank_account=self.bank_account
- )
+ bank_account=self.bank_account,
+ )
gle = get_gl_entries("Invoice Discounting", inv_disc.name)
- expected_gle = {
- inv.debit_to: [0.0, 200],
- self.ar_credit: [200, 0.0]
- }
+ 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))
def test_loan_on_submit(self):
inv = create_sales_invoice(rate=300)
- inv_disc = create_invoice_discounting([inv.name],
+ inv_disc = create_invoice_discounting(
+ [inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -67,28 +89,33 @@ def test_loan_on_submit(self):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
- period=60
- )
+ 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)
- inv_disc = create_invoice_discounting([inv.name],
+ inv_disc = create_invoice_discounting(
+ [inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
- bank_charges=100
- )
+ bank_charges=100,
+ )
je = inv_disc.create_disbursement_entry()
self.assertEqual(je.accounts[0].account, self.bank_account)
- self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount) - flt(inv_disc.bank_charges))
+ self.assertEqual(
+ je.accounts[0].debit_in_account_currency,
+ flt(inv_disc.total_amount) - flt(inv_disc.bank_charges),
+ )
self.assertEqual(je.accounts[1].account, self.bank_charges_account)
self.assertEqual(je.accounts[1].debit_in_account_currency, flt(inv_disc.bank_charges))
@@ -102,7 +129,6 @@ def test_on_disbursed(self):
self.assertEqual(je.accounts[4].account, self.ar_credit)
self.assertEqual(je.accounts[4].credit_in_account_currency, flt(inv.outstanding_amount))
-
je.posting_date = nowdate()
je.submit()
@@ -114,7 +140,8 @@ def test_on_disbursed(self):
def test_on_close_after_loan_period(self):
inv = create_sales_invoice(rate=600)
- inv_disc = create_invoice_discounting([inv.name],
+ inv_disc = create_invoice_discounting(
+ [inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -122,8 +149,8 @@ def test_on_close_after_loan_period(self):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
- period=60
- )
+ period=60,
+ )
je1 = inv_disc.create_disbursement_entry()
je1.posting_date = nowdate()
@@ -151,7 +178,8 @@ def test_on_close_after_loan_period(self):
def test_on_close_after_loan_period_after_inv_payment(self):
inv = create_sales_invoice(rate=600)
- inv_disc = create_invoice_discounting([inv.name],
+ inv_disc = create_invoice_discounting(
+ [inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -159,8 +187,8 @@ def test_on_close_after_loan_period_after_inv_payment(self):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
- period=60
- )
+ period=60,
+ )
je1 = inv_disc.create_disbursement_entry()
je1.posting_date = nowdate()
@@ -183,7 +211,8 @@ def test_on_close_after_loan_period_after_inv_payment(self):
def test_on_close_before_loan_period(self):
inv = create_sales_invoice(rate=700)
- inv_disc = create_invoice_discounting([inv.name],
+ inv_disc = create_invoice_discounting(
+ [inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -191,7 +220,7 @@ def test_on_close_before_loan_period(self):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=add_days(nowdate(), -80),
- period=60
+ period=60,
)
je1 = inv_disc.create_disbursement_entry()
@@ -209,16 +238,17 @@ def test_on_close_before_loan_period(self):
self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
def test_make_payment_before_loan_period(self):
- #it has problem
+ # it has problem
inv = create_sales_invoice(rate=700)
- inv_disc = create_invoice_discounting([inv.name],
- accounts_receivable_credit=self.ar_credit,
- accounts_receivable_discounted=self.ar_discounted,
- accounts_receivable_unpaid=self.ar_unpaid,
- short_term_loan=self.short_term_loan,
- bank_charges_account=self.bank_charges_account,
- bank_account=self.bank_account
- )
+ inv_disc = create_invoice_discounting(
+ [inv.name],
+ accounts_receivable_credit=self.ar_credit,
+ accounts_receivable_discounted=self.ar_discounted,
+ accounts_receivable_unpaid=self.ar_unpaid,
+ short_term_loan=self.short_term_loan,
+ bank_charges_account=self.bank_charges_account,
+ bank_account=self.bank_account,
+ )
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
@@ -232,26 +262,31 @@ def test_make_payment_before_loan_period(self):
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)
def test_make_payment_before_after_period(self):
- #it has problem
+ # it has problem
inv = create_sales_invoice(rate=700)
- inv_disc = create_invoice_discounting([inv.name],
- accounts_receivable_credit=self.ar_credit,
- accounts_receivable_discounted=self.ar_discounted,
- accounts_receivable_unpaid=self.ar_unpaid,
- short_term_loan=self.short_term_loan,
- bank_charges_account=self.bank_charges_account,
- bank_account=self.bank_account,
- loan_start_date=add_days(nowdate(), -10),
- period=5
- )
+ inv_disc = create_invoice_discounting(
+ [inv.name],
+ accounts_receivable_credit=self.ar_credit,
+ accounts_receivable_discounted=self.ar_discounted,
+ accounts_receivable_unpaid=self.ar_unpaid,
+ short_term_loan=self.short_term_loan,
+ bank_charges_account=self.bank_charges_account,
+ bank_account=self.bank_account,
+ loan_start_date=add_days(nowdate(), -10),
+ period=5,
+ )
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
@@ -269,9 +304,13 @@ def test_make_payment_before_after_period(self):
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)
@@ -287,17 +326,15 @@ def create_invoice_discounting(invoices, **args):
inv_disc.accounts_receivable_credit = args.accounts_receivable_credit
inv_disc.accounts_receivable_discounted = args.accounts_receivable_discounted
inv_disc.accounts_receivable_unpaid = args.accounts_receivable_unpaid
- inv_disc.short_term_loan=args.short_term_loan
- inv_disc.bank_charges_account=args.bank_charges_account
- inv_disc.bank_account=args.bank_account
+ inv_disc.short_term_loan = args.short_term_loan
+ inv_disc.bank_charges_account = args.bank_charges_account
+ inv_disc.bank_account = args.bank_account
inv_disc.loan_start_date = args.start or nowdate()
inv_disc.loan_period = args.period or 30
inv_disc.bank_charges = flt(args.bank_charges)
for d in invoices:
- inv_disc.append("invoices", {
- "sales_invoice": d
- })
+ inv_disc.append("invoices", {"sales_invoice": d})
inv_disc.insert()
if not args.do_not_submit:
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
index 0ceb6a0bc231..23f36ec6d8d0 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
@@ -13,20 +13,28 @@ def validate(self):
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)
+ abbr = frappe.get_cached_value("Company", self.company, "abbr")
+ self.name = "{0} - {1}".format(self.title, abbr)
def validate_tax_accounts(self):
"""Check whether Tax Rate is not entered twice for same Tax Type"""
check_list = []
- for d in self.get('taxes'):
+ for d in self.get("taxes"):
if d.tax_type:
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
- if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']:
+ if account_type not in [
+ "Tax",
+ "Chargeable",
+ "Income Account",
+ "Expense Account",
+ "Expenses Included In Valuation",
+ ]:
frappe.throw(
- _("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(
- d.idx))
+ _(
+ "Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable"
+ ).format(d.idx)
+ )
else:
if d.tax_type in check_list:
frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
index af01c57560fe..5a2bd720dd39 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
@@ -3,23 +3,11 @@
def get_data():
return {
- 'fieldname': 'item_tax_template',
- 'transactions': [
- {
- 'label': _('Pre Sales'),
- 'items': ['Quotation', 'Supplier Quotation']
- },
- {
- 'label': _('Sales'),
- 'items': ['Sales Invoice', 'Sales Order', 'Delivery Note']
- },
- {
- 'label': _('Purchase'),
- 'items': ['Purchase Invoice', 'Purchase Order', 'Purchase Receipt']
- },
- {
- 'label': _('Stock'),
- 'items': ['Item Groups', 'Item']
- }
- ]
+ "fieldname": "item_tax_template",
+ "transactions": [
+ {"label": _("Pre Sales"), "items": ["Quotation", "Supplier Quotation"]},
+ {"label": _("Sales"), "items": ["Sales Invoice", "Sales Order", "Delivery Note"]},
+ {"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]},
+ {"label": _("Stock"), "items": ["Item Groups", "Item"]},
+ ],
}
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index ac8ab310240d..920db5b19cb6 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -28,7 +28,9 @@
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
-class StockAccountInvalidTransaction(frappe.ValidationError): pass
+class StockAccountInvalidTransaction(frappe.ValidationError):
+ pass
+
class JournalEntry(AccountsController):
def __init__(self, *args, **kwargs):
@@ -38,11 +40,11 @@ def get_feed(self):
return self.voucher_type
def validate(self):
- if self.voucher_type == 'Opening Entry':
- self.is_opening = 'Yes'
+ if self.voucher_type == "Opening Entry":
+ self.is_opening = "Yes"
if not self.is_opening:
- self.is_opening='No'
+ self.is_opening = "No"
self.clearance_date = None
@@ -86,15 +88,17 @@ def on_submit(self):
self.update_inter_company_jv()
self.update_invoice_discounting()
self.update_status_for_full_and_final_statement()
- check_if_stock_and_account_balance_synced(self.posting_date,
- self.company, self.doctype, self.name)
+ check_if_stock_and_account_balance_synced(
+ self.posting_date, self.company, self.doctype, self.name
+ )
def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
+
unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name)
- self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+ self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
self.make_gl_entries(1)
self.update_advance_paid()
self.update_expense_claim()
@@ -127,12 +131,14 @@ def update_status_for_full_and_final_statement(self):
elif self.docstatus == 2:
frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Unpaid")
-
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")
+ account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
+ previous_account_currency = frappe.get_cached_value("Company", doc.company, "default_currency")
if account_currency == previous_account_currency:
if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
@@ -140,45 +146,63 @@ def validate_inter_company_accounts(self):
def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
for account in stock_accounts:
- account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account,
- self.posting_date, self.company)
+ account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
+ account, self.posting_date, self.company
+ )
if account_bal == stock_bal:
- frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
- .format(account), StockAccountInvalidTransaction)
+ frappe.throw(
+ _("Account: {0} can only be updated via Stock Transactions").format(account),
+ StockAccountInvalidTransaction,
+ )
def apply_tax_withholding(self):
from erpnext.accounts.report.general_ledger.general_ledger import get_account_type_map
- if not self.apply_tds or self.voucher_type not in ('Debit Note', 'Credit Note'):
+ if not self.apply_tds or self.voucher_type not in ("Debit Note", "Credit Note"):
return
- parties = [d.party for d in self.get('accounts') if d.party]
+ parties = [d.party for d in self.get("accounts") if d.party]
parties = list(set(parties))
if len(parties) > 1:
frappe.throw(_("Cannot apply TDS against multiple parties in one entry"))
account_type_map = get_account_type_map(self.company)
- party_type = 'supplier' if self.voucher_type == 'Credit Note' else 'customer'
- doctype = 'Purchase Invoice' if self.voucher_type == 'Credit Note' else 'Sales Invoice'
- debit_or_credit = 'debit_in_account_currency' if self.voucher_type == 'Credit Note' else 'credit_in_account_currency'
- rev_debit_or_credit = 'credit_in_account_currency' if debit_or_credit == 'debit_in_account_currency' else 'debit_in_account_currency'
+ party_type = "supplier" if self.voucher_type == "Credit Note" else "customer"
+ doctype = "Purchase Invoice" if self.voucher_type == "Credit Note" else "Sales Invoice"
+ debit_or_credit = (
+ "debit_in_account_currency"
+ if self.voucher_type == "Credit Note"
+ else "credit_in_account_currency"
+ )
+ rev_debit_or_credit = (
+ "credit_in_account_currency"
+ if debit_or_credit == "debit_in_account_currency"
+ else "debit_in_account_currency"
+ )
party_account = get_party_account(party_type.title(), parties[0], self.company)
- net_total = sum(d.get(debit_or_credit) for d in self.get('accounts') if account_type_map.get(d.account)
- not in ('Tax', 'Chargeable'))
+ net_total = sum(
+ d.get(debit_or_credit)
+ for d in self.get("accounts")
+ if account_type_map.get(d.account) not in ("Tax", "Chargeable")
+ )
- party_amount = sum(d.get(rev_debit_or_credit) for d in self.get('accounts') if d.account == party_account)
+ party_amount = sum(
+ d.get(rev_debit_or_credit) for d in self.get("accounts") if d.account == party_account
+ )
- inv = frappe._dict({
- party_type: parties[0],
- 'doctype': doctype,
- 'company': self.company,
- 'posting_date': self.posting_date,
- 'net_total': net_total
- })
+ inv = frappe._dict(
+ {
+ party_type: parties[0],
+ "doctype": doctype,
+ "company": self.company,
+ "posting_date": self.posting_date,
+ "net_total": net_total,
+ }
+ )
tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
@@ -186,45 +210,64 @@ def apply_tax_withholding(self):
return
accounts = []
- for d in self.get('accounts'):
- if d.get('account') == tax_withholding_details.get("account_head"):
- d.update({
- 'account': tax_withholding_details.get("account_head"),
- debit_or_credit: tax_withholding_details.get('tax_amount')
- })
+ for d in self.get("accounts"):
+ if d.get("account") == tax_withholding_details.get("account_head"):
+ d.update(
+ {
+ "account": tax_withholding_details.get("account_head"),
+ debit_or_credit: tax_withholding_details.get("tax_amount"),
+ }
+ )
- accounts.append(d.get('account'))
+ accounts.append(d.get("account"))
- if d.get('account') == party_account:
- d.update({
- rev_debit_or_credit: party_amount - tax_withholding_details.get('tax_amount')
- })
+ if d.get("account") == party_account:
+ d.update({rev_debit_or_credit: party_amount - tax_withholding_details.get("tax_amount")})
if not accounts or tax_withholding_details.get("account_head") not in accounts:
- self.append("accounts", {
- 'account': tax_withholding_details.get("account_head"),
- rev_debit_or_credit: tax_withholding_details.get('tax_amount'),
- 'against_account': parties[0]
- })
+ self.append(
+ "accounts",
+ {
+ "account": tax_withholding_details.get("account_head"),
+ rev_debit_or_credit: tax_withholding_details.get("tax_amount"),
+ "against_account": parties[0],
+ },
+ )
- to_remove = [d for d in self.get('accounts')
- if not d.get(rev_debit_or_credit) and d.account == tax_withholding_details.get("account_head")]
+ to_remove = [
+ d
+ for d in self.get("accounts")
+ if not d.get(rev_debit_or_credit) and d.account == tax_withholding_details.get("account_head")
+ ]
for d in to_remove:
self.remove(d)
def update_inter_company_jv(self):
- 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,\
- "inter_company_journal_entry_reference", self.name)
+ 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,
+ "inter_company_journal_entry_reference",
+ self.name,
+ )
def update_invoice_discounting(self):
def _validate_invoice_discounting_status(inv_disc, id_status, expected_status, row_id):
id_link = get_link_to_form("Invoice Discounting", inv_disc)
if id_status != expected_status:
- frappe.throw(_("Row #{0}: Status must be {1} for Invoice Discounting {2}").format(d.idx, expected_status, id_link))
+ frappe.throw(
+ _("Row #{0}: Status must be {1} for Invoice Discounting {2}").format(
+ d.idx, expected_status, id_link
+ )
+ )
- invoice_discounting_list = list(set([d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"]))
+ invoice_discounting_list = list(
+ set([d.reference_name for d in self.accounts if d.reference_type == "Invoice Discounting"])
+ )
for inv_disc in invoice_discounting_list:
inv_disc_doc = frappe.get_doc("Invoice Discounting", inv_disc)
status = None
@@ -248,104 +291,147 @@ def _validate_invoice_discounting_status(inv_disc, id_status, expected_status, r
if status:
inv_disc_doc.set_status(status=status)
-
def unlink_advance_entry_reference(self):
for d in self.get("accounts"):
if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
doc = frappe.get_doc(d.reference_type, d.reference_name)
doc.delink_advance_entries(self.name)
- d.reference_type = ''
- d.reference_name = ''
+ d.reference_type = ""
+ d.reference_name = ""
d.db_update()
def unlink_asset_reference(self):
for d in self.get("accounts"):
- if d.reference_type=="Asset" and d.reference_name:
+ if d.reference_type == "Asset" and d.reference_name:
asset = frappe.get_doc("Asset", d.reference_name)
for s in asset.get("schedules"):
if s.journal_entry == self.name:
s.db_set("journal_entry", None)
idx = cint(s.finance_book_id) or 1
- finance_books = asset.get('finance_books')[idx - 1]
+ finance_books = asset.get("finance_books")[idx - 1]
finance_books.value_after_depreciation += s.depreciation_amount
finance_books.db_update()
asset.set_status()
def unlink_inter_company_jv(self):
- 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,\
- "inter_company_journal_entry_reference", "")
- frappe.db.set_value("Journal Entry", self.name,\
- "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,
+ "inter_company_journal_entry_reference",
+ "",
+ )
+ frappe.db.set_value("Journal Entry", self.name, "inter_company_journal_entry_reference", "")
def unlink_asset_adjustment_entry(self):
- frappe.db.sql(""" update `tabAsset Value Adjustment`
- set journal_entry = null where journal_entry = %s""", self.name)
+ frappe.db.sql(
+ """ update `tabAsset Value Adjustment`
+ set journal_entry = null where journal_entry = %s""",
+ self.name,
+ )
def validate_party(self):
for d in self.get("accounts"):
account_type = frappe.db.get_value("Account", d.account, "account_type")
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))
+ frappe.throw(
+ _("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(
+ d.idx, d.account
+ )
+ )
def check_credit_limit(self):
- customers = list(set(d.party for d in self.get("accounts")
- if d.party_type=="Customer" and d.party and flt(d.debit) > 0))
+ customers = list(
+ set(
+ d.party
+ for d in self.get("accounts")
+ if d.party_type == "Customer" and d.party and flt(d.debit) > 0
+ )
+ )
if customers:
from erpnext.selling.doctype.customer.customer import check_credit_limit
+
for customer in customers:
check_credit_limit(customer, self.company)
def validate_cheque_info(self):
- if self.voucher_type in ['Bank Entry']:
+ if self.voucher_type in ["Bank Entry"]:
if not self.cheque_no or not self.cheque_date:
- msgprint(_("Reference No & Reference Date is required for {0}").format(self.voucher_type),
- raise_exception=1)
+ msgprint(
+ _("Reference No & Reference Date is required for {0}").format(self.voucher_type),
+ raise_exception=1,
+ )
if self.cheque_date and not self.cheque_no:
msgprint(_("Reference No is mandatory if you entered Reference Date"), raise_exception=1)
def validate_entries_for_advance(self):
- for d in self.get('accounts'):
+ for d in self.get("accounts"):
if d.reference_type not in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
- if (d.party_type == 'Customer' and flt(d.credit) > 0) or \
- (d.party_type == 'Supplier' and flt(d.debit) > 0):
- if d.is_advance=="No":
- msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account), alert=True)
+ if (d.party_type == "Customer" and flt(d.credit) > 0) or (
+ d.party_type == "Supplier" and flt(d.debit) > 0
+ ):
+ if d.is_advance == "No":
+ msgprint(
+ _(
+ "Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry."
+ ).format(d.idx, d.account),
+ alert=True,
+ )
elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes":
- frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
+ frappe.throw(
+ _(
+ "Row {0}: Payment against Sales/Purchase Order should always be marked as advance"
+ ).format(d.idx)
+ )
if d.is_advance == "Yes":
- if d.party_type == 'Customer' and flt(d.debit) > 0:
+ if d.party_type == "Customer" and flt(d.debit) > 0:
frappe.throw(_("Row {0}: Advance against Customer must be credit").format(d.idx))
- elif d.party_type == 'Supplier' and flt(d.credit) > 0:
+ elif d.party_type == "Supplier" and flt(d.credit) > 0:
frappe.throw(_("Row {0}: Advance against Supplier must be debit").format(d.idx))
def validate_against_jv(self):
- for d in self.get('accounts'):
- if d.reference_type=="Journal Entry":
+ for d in self.get("accounts"):
+ if d.reference_type == "Journal Entry":
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
if account_root_type == "Asset" and flt(d.debit) > 0:
- frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited")
- .format(d.idx, d.account))
+ frappe.throw(
+ _(
+ "Row #{0}: For {1}, you can select reference document only if account gets credited"
+ ).format(d.idx, d.account)
+ )
elif account_root_type == "Liability" and flt(d.credit) > 0:
- frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited")
- .format(d.idx, d.account))
+ frappe.throw(
+ _(
+ "Row #{0}: For {1}, you can select reference document only if account gets debited"
+ ).format(d.idx, d.account)
+ )
if d.reference_name == self.name:
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
- against_entries = frappe.db.sql("""select * from `tabJournal Entry Account`
+ against_entries = frappe.db.sql(
+ """select * from `tabJournal Entry Account`
where account = %s and docstatus = 1 and parent = %s
and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
- """, (d.account, d.reference_name), as_dict=True)
+ """,
+ (d.account, d.reference_name),
+ as_dict=True,
+ )
if not against_entries:
- frappe.throw(_("Journal Entry {0} does not have account {1} or already matched against other voucher")
- .format(d.reference_name, d.account))
+ frappe.throw(
+ _(
+ "Journal Entry {0} does not have account {1} or already matched against other voucher"
+ ).format(d.reference_name, d.account)
+ )
else:
dr_or_cr = "debit" if d.credit > 0 else "credit"
valid = False
@@ -353,16 +439,19 @@ def validate_against_jv(self):
if flt(jvd[dr_or_cr]) > 0:
valid = True
if not valid:
- frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
- .format(d.reference_name, dr_or_cr))
+ frappe.throw(
+ _("Against Journal Entry {0} does not have any unmatched {1} entry").format(
+ d.reference_name, dr_or_cr
+ )
+ )
def validate_reference_doc(self):
"""Validates reference document"""
field_dict = {
- 'Sales Invoice': ["Customer", "Debit To"],
- 'Purchase Invoice': ["Supplier", "Credit To"],
- 'Sales Order': ["Customer"],
- 'Purchase Order': ["Supplier"]
+ "Sales Invoice": ["Customer", "Debit To"],
+ "Purchase Invoice": ["Supplier", "Credit To"],
+ "Sales Order": ["Customer"],
+ "Purchase Order": ["Supplier"],
}
self.reference_totals = {}
@@ -375,56 +464,76 @@ def validate_reference_doc(self):
if not d.reference_name:
d.reference_type = None
if d.reference_type and d.reference_name and (d.reference_type in list(field_dict)):
- dr_or_cr = "credit_in_account_currency" \
- if d.reference_type in ("Sales Order", "Sales Invoice") else "debit_in_account_currency"
+ dr_or_cr = (
+ "credit_in_account_currency"
+ if d.reference_type in ("Sales Order", "Sales Invoice")
+ else "debit_in_account_currency"
+ )
# check debit or credit type Sales / Purchase Order
- if d.reference_type=="Sales Order" and flt(d.debit) > 0:
- frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type))
+ if d.reference_type == "Sales Order" and flt(d.debit) > 0:
+ frappe.throw(
+ _("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type)
+ )
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))
+ frappe.throw(
+ _("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type)
+ )
# set totals
if not d.reference_name in self.reference_totals:
self.reference_totals[d.reference_name] = 0.0
- if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'):
+ if self.voucher_type not in ("Deferred Revenue", "Deferred Expense"):
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
self.reference_types[d.reference_name] = d.reference_type
self.reference_accounts[d.reference_name] = d.account
- against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
- [scrub(dt) for dt in field_dict.get(d.reference_type)])
+ against_voucher = frappe.db.get_value(
+ d.reference_type, d.reference_name, [scrub(dt) for dt in field_dict.get(d.reference_type)]
+ )
if not against_voucher:
frappe.throw(_("Row {0}: Invalid reference {1}").format(d.idx, d.reference_name))
# 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:
- 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)
- against_voucher = ['', against_voucher[1]]
+ 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
+ )
+ against_voucher = ["", against_voucher[1]]
else:
if d.reference_type == "Sales Invoice":
- party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
+ party_account = (
+ get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
+ )
else:
party_account = against_voucher[1]
- if (against_voucher[0] != cstr(d.party) or party_account != d.account):
- frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
- .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
- d.reference_type, d.reference_name))
+ if against_voucher[0] != cstr(d.party) or party_account != d.account:
+ frappe.throw(
+ _("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}").format(
+ d.idx,
+ field_dict.get(d.reference_type)[0],
+ field_dict.get(d.reference_type)[1],
+ d.reference_type,
+ d.reference_name,
+ )
+ )
# check if party matches for Sales / Purchase Order
if d.reference_type in ("Sales Order", "Purchase Order"):
# set totals
if against_voucher != d.party:
- frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \
- .format(d.idx, d.party_type, d.party, d.reference_type))
+ frappe.throw(
+ _("Row {0}: {1} {2} does not match with {3}").format(
+ d.idx, d.party_type, d.party, d.reference_type
+ )
+ )
self.validate_orders()
self.validate_invoices()
@@ -450,62 +559,79 @@ def validate_orders(self):
account_currency = get_account_currency(account)
if account_currency == self.company_currency:
voucher_total = order.base_grand_total
- formatted_voucher_total = fmt_money(voucher_total, order.precision("base_grand_total"),
- currency=account_currency)
+ formatted_voucher_total = fmt_money(
+ voucher_total, order.precision("base_grand_total"), currency=account_currency
+ )
else:
voucher_total = order.grand_total
- formatted_voucher_total = fmt_money(voucher_total, order.precision("grand_total"),
- currency=account_currency)
+ formatted_voucher_total = fmt_money(
+ voucher_total, order.precision("grand_total"), currency=account_currency
+ )
if flt(voucher_total) < (flt(order.advance_paid) + total):
- frappe.throw(_("Advance paid against {0} {1} cannot be greater than Grand Total {2}").format(reference_type, reference_name, formatted_voucher_total))
+ frappe.throw(
+ _("Advance paid against {0} {1} cannot be greater than Grand Total {2}").format(
+ reference_type, reference_name, formatted_voucher_total
+ )
+ )
def validate_invoices(self):
"""Validate totals and docstatus for invoices"""
for reference_name, total in self.reference_totals.items():
reference_type = self.reference_types[reference_name]
- if (reference_type in ("Sales Invoice", "Purchase Invoice") and
- self.voucher_type not in ['Debit Note', 'Credit Note']):
- invoice = frappe.db.get_value(reference_type, reference_name,
- ["docstatus", "outstanding_amount"], as_dict=1)
+ if reference_type in ("Sales Invoice", "Purchase Invoice") and self.voucher_type not in [
+ "Debit Note",
+ "Credit Note",
+ ]:
+ invoice = frappe.db.get_value(
+ reference_type, reference_name, ["docstatus", "outstanding_amount"], as_dict=1
+ )
if invoice.docstatus != 1:
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
if total and flt(invoice.outstanding_amount) < total:
- frappe.throw(_("Payment against {0} {1} cannot be greater than Outstanding Amount {2}")
- .format(reference_type, reference_name, invoice.outstanding_amount))
+ frappe.throw(
+ _("Payment against {0} {1} cannot be greater than Outstanding Amount {2}").format(
+ reference_type, reference_name, invoice.outstanding_amount
+ )
+ )
def set_against_account(self):
accounts_debited, accounts_credited = [], []
- if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
- for d in self.get('accounts'):
- if d.reference_type == 'Sales Invoice':
- field = 'customer'
+ if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
+ for d in self.get("accounts"):
+ if d.reference_type == "Sales Invoice":
+ field = "customer"
else:
- field = 'supplier'
+ field = "supplier"
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
else:
for d in self.get("accounts"):
- if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
- if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
+ if flt(d.debit > 0):
+ accounts_debited.append(d.party or d.account)
+ if flt(d.credit) > 0:
+ accounts_credited.append(d.party or d.account)
for d in self.get("accounts"):
- if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
- if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
+ if flt(d.debit > 0):
+ d.against_account = ", ".join(list(set(accounts_credited)))
+ if flt(d.credit > 0):
+ d.against_account = ", ".join(list(set(accounts_debited)))
def validate_debit_credit_amount(self):
- for d in self.get('accounts'):
+ for d in self.get("accounts"):
if not flt(d.debit) and not flt(d.credit):
frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx))
def validate_total_debit_and_credit(self):
self.set_total_debit_credit()
if self.difference:
- frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}")
- .format(self.difference))
+ frappe.throw(
+ _("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference)
+ )
def set_total_debit_credit(self):
self.total_debit, self.total_credit, self.difference = 0, 0, 0
@@ -516,13 +642,16 @@ def set_total_debit_credit(self):
self.total_debit = flt(self.total_debit) + flt(d.debit, d.precision("debit"))
self.total_credit = flt(self.total_credit) + flt(d.credit, d.precision("credit"))
- self.difference = flt(self.total_debit, self.precision("total_debit")) - \
- flt(self.total_credit, self.precision("total_credit"))
+ self.difference = flt(self.total_debit, self.precision("total_debit")) - flt(
+ self.total_credit, self.precision("total_credit")
+ )
def validate_multi_currency(self):
alternate_currency = []
for d in self.get("accounts"):
- account = frappe.db.get_value("Account", d.account, ["account_currency", "account_type"], as_dict=1)
+ account = frappe.db.get_value(
+ "Account", d.account, ["account_currency", "account_type"], as_dict=1
+ )
if account:
d.account_currency = account.account_currency
d.account_type = account.account_type
@@ -541,8 +670,12 @@ def validate_multi_currency(self):
def set_amounts_in_company_currency(self):
for d in self.get("accounts"):
- d.debit_in_account_currency = flt(d.debit_in_account_currency, d.precision("debit_in_account_currency"))
- d.credit_in_account_currency = flt(d.credit_in_account_currency, d.precision("credit_in_account_currency"))
+ d.debit_in_account_currency = flt(
+ d.debit_in_account_currency, d.precision("debit_in_account_currency")
+ )
+ d.credit_in_account_currency = flt(
+ d.credit_in_account_currency, d.precision("credit_in_account_currency")
+ )
d.debit = flt(d.debit_in_account_currency * flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(d.credit_in_account_currency * flt(d.exchange_rate), d.precision("credit"))
@@ -551,13 +684,28 @@ def set_exchange_rate(self):
for d in self.get("accounts"):
if d.account_currency == self.company_currency:
d.exchange_rate = 1
- elif not d.exchange_rate or d.exchange_rate == 1 or \
- (d.reference_type in ("Sales Invoice", "Purchase Invoice")
- and d.reference_name and self.posting_date):
-
- # Modified to include the posting date for which to retreive the exchange rate
- d.exchange_rate = get_exchange_rate(self.posting_date, d.account, d.account_currency,
- self.company, d.reference_type, d.reference_name, d.debit, d.credit, d.exchange_rate)
+ elif (
+ not d.exchange_rate
+ or d.exchange_rate == 1
+ or (
+ d.reference_type in ("Sales Invoice", "Purchase Invoice")
+ and d.reference_name
+ and self.posting_date
+ )
+ ):
+
+ # Modified to include the posting date for which to retreive the exchange rate
+ d.exchange_rate = get_exchange_rate(
+ self.posting_date,
+ d.account,
+ d.account_currency,
+ self.company,
+ d.reference_type,
+ d.reference_name,
+ d.debit,
+ d.credit,
+ d.exchange_rate,
+ )
if not d.exchange_rate:
frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
@@ -570,55 +718,76 @@ def create_remarks(self):
if self.cheque_no:
if self.cheque_date:
- r.append(_('Reference #{0} dated {1}').format(self.cheque_no, formatdate(self.cheque_date)))
+ r.append(_("Reference #{0} dated {1}").format(self.cheque_no, formatdate(self.cheque_date)))
else:
msgprint(_("Please enter Reference date"), raise_exception=frappe.MandatoryError)
- for d in self.get('accounts'):
- if d.reference_type=="Sales Invoice" and d.credit:
- r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
- d.reference_name))
+ for d in self.get("accounts"):
+ if d.reference_type == "Sales Invoice" and d.credit:
+ r.append(
+ _("{0} against Sales Invoice {1}").format(
+ fmt_money(flt(d.credit), currency=self.company_currency), d.reference_name
+ )
+ )
- if d.reference_type=="Sales Order" and d.credit:
- r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
- d.reference_name))
+ if d.reference_type == "Sales Order" and d.credit:
+ r.append(
+ _("{0} against Sales Order {1}").format(
+ fmt_money(flt(d.credit), currency=self.company_currency), d.reference_name
+ )
+ )
if d.reference_type == "Purchase Invoice" and d.debit:
- bill_no = frappe.db.sql("""select bill_no, bill_date
- from `tabPurchase Invoice` where name=%s""", d.reference_name)
- if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
- not in ['na', 'not applicable', 'none']:
- r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=self.company_currency), bill_no[0][0],
- bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
+ bill_no = frappe.db.sql(
+ """select bill_no, bill_date
+ from `tabPurchase Invoice` where name=%s""",
+ d.reference_name,
+ )
+ if (
+ bill_no
+ and bill_no[0][0]
+ and bill_no[0][0].lower().strip() not in ["na", "not applicable", "none"]
+ ):
+ r.append(
+ _("{0} against Bill {1} dated {2}").format(
+ fmt_money(flt(d.debit), currency=self.company_currency),
+ bill_no[0][0],
+ bill_no[0][1] and formatdate(bill_no[0][1].strftime("%Y-%m-%d")),
+ )
+ )
if d.reference_type == "Purchase Order" and d.debit:
- r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
- d.reference_name))
+ r.append(
+ _("{0} against Purchase Order {1}").format(
+ fmt_money(flt(d.credit), currency=self.company_currency), d.reference_name
+ )
+ )
if r:
- self.remark = ("\n").join(r) #User Remarks is not mandatory
+ self.remark = ("\n").join(r) # User Remarks is not mandatory
def set_print_format_fields(self):
bank_amount = party_amount = total_amount = 0.0
- currency = bank_account_currency = party_account_currency = pay_to_recd_from= None
+ currency = bank_account_currency = party_account_currency = pay_to_recd_from = None
party_type = None
- for d in self.get('accounts'):
- if d.party_type in ['Customer', 'Supplier'] and d.party:
+ for d in self.get("accounts"):
+ if d.party_type in ["Customer", "Supplier"] and d.party:
party_type = d.party_type
if not pay_to_recd_from:
pay_to_recd_from = d.party
if pay_to_recd_from and pay_to_recd_from == d.party:
- party_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
+ party_amount += d.debit_in_account_currency or d.credit_in_account_currency
party_account_currency = d.account_currency
elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
- bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
+ bank_amount += d.debit_in_account_currency or d.credit_in_account_currency
bank_account_currency = d.account_currency
if party_type and pay_to_recd_from:
- self.pay_to_recd_from = frappe.db.get_value(party_type, pay_to_recd_from,
- "customer_name" if party_type=="Customer" else "supplier_name")
+ self.pay_to_recd_from = frappe.db.get_value(
+ party_type, pay_to_recd_from, "customer_name" if party_type == "Customer" else "supplier_name"
+ )
if bank_amount:
total_amount = bank_amount
currency = bank_account_currency
@@ -632,6 +801,7 @@ def set_total_amount(self, amt, currency):
self.total_amount = amt
self.total_amount_currency = currency
from frappe.utils import money_in_words
+
self.total_amount_in_words = money_in_words(amt, currency)
def make_gl_entries(self, cancel=0, adv_adj=0):
@@ -645,38 +815,45 @@ def make_gl_entries(self, cancel=0, adv_adj=0):
remarks = "\n".join(r)
gl_map.append(
- self.get_gl_dict({
- "account": d.account,
- "party_type": d.party_type,
- "due_date": self.due_date,
- "party": d.party,
- "against": d.against_account,
- "debit": flt(d.debit, d.precision("debit")),
- "credit": flt(d.credit, d.precision("credit")),
- "account_currency": d.account_currency,
- "debit_in_account_currency": flt(d.debit_in_account_currency, d.precision("debit_in_account_currency")),
- "credit_in_account_currency": flt(d.credit_in_account_currency, d.precision("credit_in_account_currency")),
- "against_voucher_type": d.reference_type,
- "against_voucher": d.reference_name,
- "remarks": remarks,
- "voucher_detail_no": d.reference_detail_no,
- "cost_center": d.cost_center,
- "project": d.project,
- "finance_book": self.finance_book
- }, item=d)
+ self.get_gl_dict(
+ {
+ "account": d.account,
+ "party_type": d.party_type,
+ "due_date": self.due_date,
+ "party": d.party,
+ "against": d.against_account,
+ "debit": flt(d.debit, d.precision("debit")),
+ "credit": flt(d.credit, d.precision("credit")),
+ "account_currency": d.account_currency,
+ "debit_in_account_currency": flt(
+ d.debit_in_account_currency, d.precision("debit_in_account_currency")
+ ),
+ "credit_in_account_currency": flt(
+ d.credit_in_account_currency, d.precision("credit_in_account_currency")
+ ),
+ "against_voucher_type": d.reference_type,
+ "against_voucher": d.reference_name,
+ "remarks": remarks,
+ "voucher_detail_no": d.reference_detail_no,
+ "cost_center": d.cost_center,
+ "project": d.project,
+ "finance_book": self.finance_book,
+ },
+ item=d,
+ )
)
- if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
- update_outstanding = 'No'
+ if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
+ update_outstanding = "No"
else:
- update_outstanding = 'Yes'
+ update_outstanding = "Yes"
if gl_map:
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
@frappe.whitelist()
def get_balance(self):
- if not self.get('accounts'):
+ if not self.get("accounts"):
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
else:
self.total_debit, self.total_credit = 0, 0
@@ -685,18 +862,18 @@ def get_balance(self):
# If any row without amount, set the diff on that row
if diff:
blank_row = None
- for d in self.get('accounts'):
+ for d in self.get("accounts"):
if not d.credit_in_account_currency and not d.debit_in_account_currency and diff != 0:
blank_row = d
if not blank_row:
- blank_row = self.append('accounts', {})
+ blank_row = self.append("accounts", {})
blank_row.exchange_rate = 1
- if diff>0:
+ if diff > 0:
blank_row.credit_in_account_currency = diff
blank_row.credit = diff
- elif diff<0:
+ elif diff < 0:
blank_row.debit_in_account_currency = abs(diff)
blank_row.debit = abs(diff)
@@ -704,76 +881,100 @@ def get_balance(self):
@frappe.whitelist()
def get_outstanding_invoices(self):
- self.set('accounts', [])
+ self.set("accounts", [])
total = 0
for d in self.get_values():
total += flt(d.outstanding_amount, self.precision("credit", "accounts"))
- jd1 = self.append('accounts', {})
+ jd1 = self.append("accounts", {})
jd1.account = d.account
jd1.party = d.party
- if self.write_off_based_on == 'Accounts Receivable':
+ if self.write_off_based_on == "Accounts Receivable":
jd1.party_type = "Customer"
- jd1.credit_in_account_currency = flt(d.outstanding_amount, self.precision("credit", "accounts"))
+ jd1.credit_in_account_currency = flt(
+ d.outstanding_amount, self.precision("credit", "accounts")
+ )
jd1.reference_type = "Sales Invoice"
jd1.reference_name = cstr(d.name)
- elif self.write_off_based_on == 'Accounts Payable':
+ elif self.write_off_based_on == "Accounts Payable":
jd1.party_type = "Supplier"
jd1.debit_in_account_currency = flt(d.outstanding_amount, self.precision("debit", "accounts"))
jd1.reference_type = "Purchase Invoice"
jd1.reference_name = cstr(d.name)
- jd2 = self.append('accounts', {})
- if self.write_off_based_on == 'Accounts Receivable':
+ jd2 = self.append("accounts", {})
+ if self.write_off_based_on == "Accounts Receivable":
jd2.debit_in_account_currency = total
- elif self.write_off_based_on == 'Accounts Payable':
+ elif self.write_off_based_on == "Accounts Payable":
jd2.credit_in_account_currency = total
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 = (
+ " and outstanding_amount <= {0}".format(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
+ 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), 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
+ and outstanding_amount > 0 %s"""
+ % ("%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), self.company, as_dict=True)
+ and outstanding_amount > 0 %s"""
+ % ("%s", cond),
+ self.company,
+ as_dict=True,
+ )
def update_expense_claim(self):
for d in self.accounts:
- if d.reference_type=="Expense Claim" and d.reference_name:
+ if d.reference_type == "Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
if self.docstatus == 2:
update_reimbursed_amount(doc, -1 * d.debit)
else:
update_reimbursed_amount(doc, d.debit)
-
def validate_expense_claim(self):
for d in self.accounts:
- if d.reference_type=="Expense Claim":
- sanctioned_amount, reimbursed_amount = frappe.db.get_value("Expense Claim",
- d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed"))
+ if d.reference_type == "Expense Claim":
+ sanctioned_amount, reimbursed_amount = frappe.db.get_value(
+ "Expense Claim", d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed")
+ )
pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount)
if d.debit > pending_amount:
- frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}").format(d.idx, d.reference_name, pending_amount))
+ frappe.throw(
+ _(
+ "Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}"
+ ).format(d.idx, d.reference_name, pending_amount)
+ )
def validate_credit_debit_note(self):
if self.stock_entry:
if frappe.db.get_value("Stock Entry", self.stock_entry, "docstatus") != 1:
frappe.throw(_("Stock Entry {0} is not submitted").format(self.stock_entry))
- if frappe.db.exists({"doctype": "Journal Entry", "stock_entry": self.stock_entry, "docstatus":1}):
- frappe.msgprint(_("Warning: Another {0} # {1} exists against stock entry {2}").format(self.voucher_type, self.name, self.stock_entry))
+ if frappe.db.exists(
+ {"doctype": "Journal Entry", "stock_entry": self.stock_entry, "docstatus": 1}
+ ):
+ frappe.msgprint(
+ _("Warning: Another {0} # {1} exists against stock entry {2}").format(
+ self.voucher_type, self.name, self.stock_entry
+ )
+ )
def validate_empty_accounts_table(self):
- if not self.get('accounts'):
+ if not self.get("accounts"):
frappe.throw(_("Accounts table cannot be blank."))
def set_account_and_party_balance(self):
@@ -784,54 +985,66 @@ def set_account_and_party_balance(self):
account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date)
if (d.party_type, d.party) not in party_balance:
- party_balance[(d.party_type, d.party)] = get_balance_on(party_type=d.party_type,
- party=d.party, date=self.posting_date, company=self.company)
+ party_balance[(d.party_type, d.party)] = get_balance_on(
+ party_type=d.party_type, party=d.party, date=self.posting_date, company=self.company
+ )
d.account_balance = account_balance[d.account]
d.party_balance = party_balance[(d.party_type, d.party)]
+
@frappe.whitelist()
def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
+
if mode_of_payment:
account = get_bank_cash_account(mode_of_payment, company).get("account")
if not account:
- '''
- Set the default account first. If the user hasn't set any default account then, he doesn't
- want us to set any random account. In this case set the account only if there is single
- account (of that type), otherwise return empty dict.
- '''
- if account_type=="Bank":
- account = frappe.get_cached_value('Company', company, "default_bank_account")
+ """
+ Set the default account first. If the user hasn't set any default account then, he doesn't
+ want us to set any random account. In this case set the account only if there is single
+ account (of that type), otherwise return empty dict.
+ """
+ if account_type == "Bank":
+ account = frappe.get_cached_value("Company", company, "default_bank_account")
if not account:
- account_list = frappe.get_all("Account", filters = {"company": company,
- "account_type": "Bank", "is_group": 0})
+ account_list = frappe.get_all(
+ "Account", filters={"company": company, "account_type": "Bank", "is_group": 0}
+ )
if len(account_list) == 1:
account = account_list[0].name
- elif account_type=="Cash":
- account = frappe.get_cached_value('Company', company, "default_cash_account")
+ elif account_type == "Cash":
+ account = frappe.get_cached_value("Company", company, "default_cash_account")
if not account:
- account_list = frappe.get_all("Account", filters = {"company": company,
- "account_type": "Cash", "is_group": 0})
+ account_list = frappe.get_all(
+ "Account", filters={"company": company, "account_type": "Cash", "is_group": 0}
+ )
if len(account_list) == 1:
account = account_list[0].name
if account:
- account_details = frappe.db.get_value("Account", account,
- ["account_currency", "account_type"], as_dict=1)
+ account_details = frappe.db.get_value(
+ "Account", account, ["account_currency", "account_type"], as_dict=1
+ )
+
+ return frappe._dict(
+ {
+ "account": account,
+ "balance": get_balance_on(account),
+ "account_currency": account_details.account_currency,
+ "account_type": account_details.account_type,
+ }
+ )
+ else:
+ return frappe._dict()
- return frappe._dict({
- "account": account,
- "balance": get_balance_on(account),
- "account_currency": account_details.account_currency,
- "account_type": account_details.account_type
- })
- else: return frappe._dict()
@frappe.whitelist()
-def get_payment_entry_against_order(dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None):
+def get_payment_entry_against_order(
+ dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
+):
ref_doc = frappe.get_doc(dt, dn)
if flt(ref_doc.per_billed, 2) > 0:
@@ -855,22 +1068,28 @@ def get_payment_entry_against_order(dt, dn, amount=None, debit_in_account_curren
else:
amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
- return get_payment_entry(ref_doc, {
- "party_type": party_type,
- "party_account": party_account,
- "party_account_currency": party_account_currency,
- "amount_field_party": amount_field_party,
- "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),
- "is_advance": "Yes",
- "bank_account": bank_account,
- "journal_entry": journal_entry
- })
+ return get_payment_entry(
+ ref_doc,
+ {
+ "party_type": party_type,
+ "party_account": party_account,
+ "party_account_currency": party_account_currency,
+ "amount_field_party": amount_field_party,
+ "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),
+ "is_advance": "Yes",
+ "bank_account": bank_account,
+ "journal_entry": journal_entry,
+ },
+ )
+
@frappe.whitelist()
-def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None):
+def get_payment_entry_against_invoice(
+ dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
+):
ref_doc = frappe.get_doc(dt, dn)
if dt == "Sales Invoice":
party_type = "Customer"
@@ -879,73 +1098,91 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur
party_type = "Supplier"
party_account = ref_doc.credit_to
- if (dt == "Sales Invoice" and ref_doc.outstanding_amount > 0) \
- or (dt == "Purchase Invoice" and ref_doc.outstanding_amount < 0):
- amount_field_party = "credit_in_account_currency"
- amount_field_bank = "debit_in_account_currency"
+ if (dt == "Sales Invoice" and ref_doc.outstanding_amount > 0) or (
+ dt == "Purchase Invoice" and ref_doc.outstanding_amount < 0
+ ):
+ amount_field_party = "credit_in_account_currency"
+ amount_field_bank = "debit_in_account_currency"
else:
amount_field_party = "debit_in_account_currency"
amount_field_bank = "credit_in_account_currency"
- return get_payment_entry(ref_doc, {
- "party_type": party_type,
- "party_account": party_account,
- "party_account_currency": ref_doc.party_account_currency,
- "amount_field_party": amount_field_party,
- "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),
- "is_advance": "No",
- "bank_account": bank_account,
- "journal_entry": journal_entry
- })
+ return get_payment_entry(
+ ref_doc,
+ {
+ "party_type": party_type,
+ "party_account": party_account,
+ "party_account_currency": ref_doc.party_account_currency,
+ "amount_field_party": amount_field_party,
+ "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),
+ "is_advance": "No",
+ "bank_account": bank_account,
+ "journal_entry": journal_entry,
+ },
+ )
+
def get_payment_entry(ref_doc, args):
- cost_center = ref_doc.get("cost_center") or frappe.get_cached_value('Company', ref_doc.company, "cost_center")
+ cost_center = ref_doc.get("cost_center") or frappe.get_cached_value(
+ "Company", ref_doc.company, "cost_center"
+ )
exchange_rate = 1
if args.get("party_account"):
# Modified to include the posting date for which the exchange rate is required.
# Assumed to be the posting date in the reference document
- exchange_rate = get_exchange_rate(ref_doc.get("posting_date") or ref_doc.get("transaction_date"),
- args.get("party_account"), args.get("party_account_currency"),
- ref_doc.company, ref_doc.doctype, ref_doc.name)
+ exchange_rate = get_exchange_rate(
+ ref_doc.get("posting_date") or ref_doc.get("transaction_date"),
+ args.get("party_account"),
+ args.get("party_account_currency"),
+ ref_doc.company,
+ ref_doc.doctype,
+ ref_doc.name,
+ )
je = frappe.new_doc("Journal Entry")
- je.update({
- "voucher_type": "Bank Entry",
- "company": ref_doc.company,
- "remark": args.get("remarks")
- })
-
- party_row = je.append("accounts", {
- "account": args.get("party_account"),
- "party_type": args.get("party_type"),
- "party": ref_doc.get(args.get("party_type").lower()),
- "cost_center": cost_center,
- "account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
- "account_currency": args.get("party_account_currency") or \
- get_account_currency(args.get("party_account")),
- "balance": get_balance_on(args.get("party_account")),
- "party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
- "exchange_rate": exchange_rate,
- args.get("amount_field_party"): args.get("amount"),
- "is_advance": args.get("is_advance"),
- "reference_type": ref_doc.doctype,
- "reference_name": ref_doc.name
- })
+ je.update(
+ {"voucher_type": "Bank Entry", "company": ref_doc.company, "remark": args.get("remarks")}
+ )
+
+ party_row = je.append(
+ "accounts",
+ {
+ "account": args.get("party_account"),
+ "party_type": args.get("party_type"),
+ "party": ref_doc.get(args.get("party_type").lower()),
+ "cost_center": cost_center,
+ "account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
+ "account_currency": args.get("party_account_currency")
+ or get_account_currency(args.get("party_account")),
+ "balance": get_balance_on(args.get("party_account")),
+ "party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
+ "exchange_rate": exchange_rate,
+ args.get("amount_field_party"): args.get("amount"),
+ "is_advance": args.get("is_advance"),
+ "reference_type": ref_doc.doctype,
+ "reference_name": ref_doc.name,
+ },
+ )
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.
# Assumed to be the posting date of the reference date
- bank_row.exchange_rate = get_exchange_rate(ref_doc.get("posting_date")
- or ref_doc.get("transaction_date"), bank_account["account"],
- bank_account["account_currency"], ref_doc.company)
+ bank_row.exchange_rate = get_exchange_rate(
+ ref_doc.get("posting_date") or ref_doc.get("transaction_date"),
+ bank_account["account"],
+ bank_account["account_currency"],
+ ref_doc.company,
+ )
bank_row.cost_center = cost_center
@@ -957,9 +1194,10 @@ def get_payment_entry(ref_doc, args):
bank_row.set(args.get("amount_field_bank"), amount * exchange_rate)
# Multi currency check again
- if party_row.account_currency != ref_doc.company_currency \
- or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency):
- je.multi_currency = 1
+ if party_row.account_currency != ref_doc.company_currency or (
+ bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency
+ ):
+ je.multi_currency = 1
je.set_amounts_in_company_currency()
je.set_total_debit_credit()
@@ -970,13 +1208,17 @@ def get_payment_entry(ref_doc, args):
@frappe.whitelist()
def get_opening_accounts(company):
"""get all balance sheet accounts for opening entry"""
- accounts = frappe.db.sql_list("""select
+ accounts = frappe.db.sql_list(
+ """select
name from tabAccount
where
is_group=0 and report_type='Balance Sheet' and company={0} and
name not in (select distinct account from tabWarehouse where
account is not null and account != '')
- order by name asc""".format(frappe.db.escape(company)))
+ order by name asc""".format(
+ frappe.db.escape(company)
+ )
+ )
return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
@@ -984,10 +1226,11 @@ def get_opening_accounts(company):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
- if not frappe.db.has_column('Journal Entry', searchfield):
+ if not frappe.db.has_column("Journal Entry", searchfield):
return []
- return frappe.db.sql("""
+ return frappe.db.sql(
+ """
SELECT jv.name, jv.posting_date, jv.user_remark
FROM `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
WHERE jv_detail.parent = jv.name
@@ -1001,14 +1244,17 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
AND jv.`{0}` LIKE %(txt)s
ORDER BY jv.name DESC
LIMIT %(offset)s, %(limit)s
- """.format(searchfield), dict(
- account=filters.get("account"),
- party=cstr(filters.get("party")),
- txt="%{0}%".format(txt),
- offset=start,
- limit=page_len
- )
- )
+ """.format(
+ searchfield
+ ),
+ dict(
+ account=filters.get("account"),
+ party=cstr(filters.get("party")),
+ txt="%{0}%".format(txt),
+ offset=start,
+ limit=page_len,
+ ),
+ )
@frappe.whitelist()
@@ -1024,37 +1270,55 @@ def get_outstanding(args):
if args.get("doctype") == "Journal Entry":
condition = " and party=%(party)s" if args.get("party") else ""
- against_jv_amount = frappe.db.sql("""
+ against_jv_amount = frappe.db.sql(
+ """
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), args)
+ and (reference_type is null or reference_type = '')""".format(
+ condition
+ ),
+ 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"
- return {
- amount_field: abs(against_jv_amount)
- }
+ 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"
- invoice = frappe.db.get_value(args["doctype"], args["docname"],
- ["outstanding_amount", "conversion_rate", scrub(party_type)], as_dict=1)
+ invoice = frappe.db.get_value(
+ args["doctype"],
+ args["docname"],
+ ["outstanding_amount", "conversion_rate", scrub(party_type)],
+ as_dict=1,
+ )
- 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 = "credit_in_account_currency" \
- if flt(invoice.outstanding_amount) > 0 else "debit_in_account_currency"
+ amount_field = (
+ "credit_in_account_currency"
+ if flt(invoice.outstanding_amount) > 0
+ else "debit_in_account_currency"
+ )
else:
- amount_field = "debit_in_account_currency" \
- if flt(invoice.outstanding_amount) > 0 else "credit_in_account_currency"
+ amount_field = (
+ "debit_in_account_currency"
+ if flt(invoice.outstanding_amount) > 0
+ else "credit_in_account_currency"
+ )
return {
amount_field: abs(flt(invoice.outstanding_amount)),
"exchange_rate": exchange_rate,
"party_type": party_type,
- "party": invoice.get(scrub(party_type))
+ "party": invoice.get(scrub(party_type)),
}
+
@frappe.whitelist()
def get_party_account_and_balance(company, party_type, party, cost_center=None):
if not frappe.has_permission("Account"):
@@ -1063,24 +1327,30 @@ def get_party_account_and_balance(company, party_type, party, cost_center=None):
account = get_party_account(party_type, party, company)
account_balance = get_balance_on(account=account, cost_center=cost_center)
- party_balance = get_balance_on(party_type=party_type, party=party, company=company, cost_center=cost_center)
+ party_balance = get_balance_on(
+ party_type=party_type, party=party, company=company, cost_center=cost_center
+ )
return {
"account": account,
"balance": account_balance,
"party_balance": party_balance,
- "account_currency": frappe.db.get_value("Account", account, "account_currency")
+ "account_currency": frappe.db.get_value("Account", account, "account_currency"),
}
@frappe.whitelist()
-def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None):
+def get_account_balance_and_party_type(
+ account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None
+):
"""Returns dict of account balance 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)
company_currency = erpnext.get_company_currency(company)
- account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
+ account_details = frappe.db.get_value(
+ "Account", account, ["account_type", "account_currency"], as_dict=1
+ )
if not account_details:
return
@@ -1097,11 +1367,17 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi
"party_type": party_type,
"account_type": account_details.account_type,
"account_currency": account_details.account_currency or company_currency,
-
# The date used to retreive the exchange rate here is the date passed in
# as an argument to this function. It is assumed to be the date on which the balance is sought
- "exchange_rate": get_exchange_rate(date, account, account_details.account_currency,
- company, debit=debit, credit=credit, exchange_rate=exchange_rate)
+ "exchange_rate": get_exchange_rate(
+ date,
+ account,
+ account_details.account_currency,
+ company,
+ debit=debit,
+ credit=credit,
+ exchange_rate=exchange_rate,
+ ),
}
# un-set party if not party type
@@ -1112,11 +1388,22 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi
@frappe.whitelist()
-def get_exchange_rate(posting_date, account=None, account_currency=None, company=None,
- reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
+def get_exchange_rate(
+ posting_date,
+ account=None,
+ account_currency=None,
+ company=None,
+ reference_type=None,
+ reference_name=None,
+ debit=None,
+ credit=None,
+ exchange_rate=None,
+):
from erpnext.setup.utils import get_exchange_rate
- account_details = frappe.db.get_value("Account", account,
- ["account_type", "root_type", "account_currency", "company"], as_dict=1)
+
+ account_details = frappe.db.get_value(
+ "Account", account, ["account_type", "root_type", "account_currency", "company"], as_dict=1
+ )
if not account_details:
frappe.throw(_("Please select correct account"))
@@ -1135,7 +1422,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
# The date used to retreive the exchange rate here is the date passed
# in as an argument to this function.
- elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date:
+ elif (not exchange_rate or flt(exchange_rate) == 1) and account_currency and posting_date:
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
else:
exchange_rate = 1
@@ -1154,15 +1441,17 @@ def get_average_exchange_rate(account):
return exchange_rate
+
@frappe.whitelist()
def make_inter_company_journal_entry(name, voucher_type, company):
- journal_entry = frappe.new_doc('Journal Entry')
+ journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = voucher_type
journal_entry.company = company
journal_entry.posting_date = nowdate()
journal_entry.inter_company_journal_entry_reference = name
return journal_entry.as_dict()
+
@frappe.whitelist()
def make_reverse_journal_entry(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
@@ -1170,24 +1459,25 @@ def make_reverse_journal_entry(source_name, target_doc=None):
def post_process(source, target):
target.reversal_of = source.name
- doclist = get_mapped_doc("Journal Entry", source_name, {
- "Journal Entry": {
- "doctype": "Journal Entry",
- "validation": {
- "docstatus": ["=", 1]
- }
- },
- "Journal Entry Account": {
- "doctype": "Journal Entry Account",
- "field_map": {
- "account_currency": "account_currency",
- "exchange_rate": "exchange_rate",
- "debit_in_account_currency": "credit_in_account_currency",
- "debit": "credit",
- "credit_in_account_currency": "debit_in_account_currency",
- "credit": "debit",
- }
+ doclist = get_mapped_doc(
+ "Journal Entry",
+ source_name,
+ {
+ "Journal Entry": {"doctype": "Journal Entry", "validation": {"docstatus": ["=", 1]}},
+ "Journal Entry Account": {
+ "doctype": "Journal Entry Account",
+ "field_map": {
+ "account_currency": "account_currency",
+ "exchange_rate": "exchange_rate",
+ "debit_in_account_currency": "credit_in_account_currency",
+ "debit": "credit",
+ "credit_in_account_currency": "debit_in_account_currency",
+ "credit": "debit",
+ },
+ },
},
- }, target_doc, post_process)
+ target_doc,
+ post_process,
+ )
return doclist
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index 481462b2abaa..2cc5378e927e 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -39,14 +39,25 @@ def jv_against_voucher_testcase(self, base_jv, test_voucher):
test_voucher.submit()
if test_voucher.doctype == "Journal Entry":
- self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
+ self.assertTrue(
+ frappe.db.sql(
+ """select name from `tabJournal Entry Account`
where account = %s and docstatus = 1 and parent = %s""",
- ("_Test Receivable - _TC", test_voucher.name)))
-
- self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
- where reference_type = %s and reference_name = %s""", (test_voucher.doctype, test_voucher.name)))
-
- base_jv.get("accounts")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
+ ("_Test Receivable - _TC", test_voucher.name),
+ )
+ )
+
+ self.assertFalse(
+ frappe.db.sql(
+ """select name from `tabJournal Entry Account`
+ where reference_type = %s and reference_name = %s""",
+ (test_voucher.doctype, test_voucher.name),
+ )
+ )
+
+ base_jv.get("accounts")[0].is_advance = (
+ "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
+ )
base_jv.get("accounts")[0].set("reference_type", test_voucher.doctype)
base_jv.get("accounts")[0].set("reference_name", test_voucher.name)
base_jv.insert()
@@ -54,18 +65,28 @@ def jv_against_voucher_testcase(self, base_jv, test_voucher):
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
- 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),
- (submitted_voucher.doctype, submitted_voucher.name)))
+ 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
+ ),
+ (submitted_voucher.doctype, submitted_voucher.name),
+ )
+ )
if base_jv.get("accounts")[0].is_advance == "Yes":
self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
self.cancel_against_voucher_testcase(submitted_voucher)
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'), (test_voucher.name))
+ # Test advance paid field
+ advance_paid = frappe.db.sql(
+ """select advance_paid from `tab%s`
+ where name=%s"""
+ % (test_voucher.doctype, "%s"),
+ (test_voucher.name),
+ )
payment_against_order = base_jv.get("accounts")[0].get(dr_or_cr)
self.assertTrue(flt(advance_paid[0][0]) == flt(payment_against_order))
@@ -74,13 +95,19 @@ def cancel_against_voucher_testcase(self, test_voucher):
if test_voucher.doctype == "Journal Entry":
# if test_voucher is a Journal Entry, test cancellation of test_voucher
test_voucher.cancel()
- self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
- where reference_type='Journal Entry' and reference_name=%s""", test_voucher.name))
+ self.assertFalse(
+ frappe.db.sql(
+ """select name from `tabJournal Entry Account`
+ where reference_type='Journal Entry' and reference_name=%s""",
+ test_voucher.name,
+ )
+ )
elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
- frappe.db.set_value("Accounts Settings", "Accounts Settings",
- "unlink_advance_payment_on_cancelation_of_order", 0)
+ frappe.db.set_value(
+ "Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0
+ )
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
self.assertRaises(frappe.LinkExistsError, submitted_voucher.cancel)
@@ -89,7 +116,10 @@ def test_jv_against_stock_account(self):
stock_account = get_inventory_account(company)
from erpnext.accounts.utils import get_stock_and_account_balance
- account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(stock_account, nowdate(), company)
+
+ account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
+ stock_account, nowdate(), company
+ )
diff = flt(account_bal) - flt(stock_bal)
if not diff:
@@ -98,19 +128,25 @@ def test_jv_against_stock_account(self):
jv = frappe.new_doc("Journal Entry")
jv.company = company
jv.posting_date = nowdate()
- jv.append("accounts", {
- "account": stock_account,
- "cost_center": "Main - TCP1",
- "debit_in_account_currency": 0 if diff > 0 else abs(diff),
- "credit_in_account_currency": diff if diff > 0 else 0
- })
-
- jv.append("accounts", {
- "account": "Stock Adjustment - TCP1",
- "cost_center": "Main - TCP1",
- "debit_in_account_currency": diff if diff > 0 else 0,
- "credit_in_account_currency": 0 if diff > 0 else abs(diff)
- })
+ jv.append(
+ "accounts",
+ {
+ "account": stock_account,
+ "cost_center": "Main - TCP1",
+ "debit_in_account_currency": 0 if diff > 0 else abs(diff),
+ "credit_in_account_currency": diff if diff > 0 else 0,
+ },
+ )
+
+ jv.append(
+ "accounts",
+ {
+ "account": "Stock Adjustment - TCP1",
+ "cost_center": "Main - TCP1",
+ "debit_in_account_currency": diff if diff > 0 else 0,
+ "credit_in_account_currency": 0 if diff > 0 else abs(diff),
+ },
+ )
jv.insert()
if account_bal == stock_bal:
@@ -121,16 +157,21 @@ def test_jv_against_stock_account(self):
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()
- gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+ gl_entries = frappe.db.sql(
+ """select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""", jv.name, as_dict=1)
+ order by account asc""",
+ jv.name,
+ as_dict=1,
+ )
self.assertTrue(gl_entries)
@@ -140,33 +181,42 @@ def test_multi_currency(self):
"debit": 5000,
"debit_in_account_currency": 100,
"credit": 0,
- "credit_in_account_currency": 0
+ "credit_in_account_currency": 0,
},
"_Test Bank - _TC": {
"account_currency": "INR",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
- "credit_in_account_currency": 5000
- }
+ "credit_in_account_currency": 5000,
+ },
}
- for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+ for field in (
+ "account_currency",
+ "debit",
+ "debit_in_account_currency",
+ "credit",
+ "credit_in_account_currency",
+ ):
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field])
# cancel
jv.cancel()
- gle = frappe.db.sql("""select name from `tabGL Entry`
- where voucher_type='Sales Invoice' and voucher_no=%s""", jv.name)
+ gle = frappe.db.sql(
+ """select name from `tabGL Entry`
+ where voucher_type='Sales Invoice' and voucher_no=%s""",
+ jv.name,
+ )
self.assertFalse(gle)
def test_reverse_journal_entry(self):
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
- jv = make_journal_entry("_Test Bank USD - _TC",
- "Sales - _TC", 100, exchange_rate=50, save=False)
+
+ jv = make_journal_entry("_Test Bank USD - _TC", "Sales - _TC", 100, exchange_rate=50, save=False)
jv.get("accounts")[1].credit_in_account_currency = 5000
jv.get("accounts")[1].exchange_rate = 1
@@ -176,15 +226,17 @@ def test_reverse_journal_entry(self):
rjv.posting_date = nowdate()
rjv.submit()
-
- gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+ gl_entries = frappe.db.sql(
+ """select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""", rjv.name, as_dict=1)
+ order by account asc""",
+ rjv.name,
+ as_dict=1,
+ )
self.assertTrue(gl_entries)
-
expected_values = {
"_Test Bank USD - _TC": {
"account_currency": "USD",
@@ -199,44 +251,38 @@ def test_reverse_journal_entry(self):
"debit_in_account_currency": 5000,
"credit": 0,
"credit_in_account_currency": 0,
- }
+ },
}
- for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+ for field in (
+ "account_currency",
+ "debit",
+ "debit_in_account_currency",
+ "credit",
+ "credit_in_account_currency",
+ ):
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field])
def test_disallow_change_in_account_currency_for_a_party(self):
# create jv in USD
- jv = make_journal_entry("_Test Bank USD - _TC",
- "_Test Receivable USD - _TC", 100, save=False)
+ jv = make_journal_entry("_Test Bank USD - _TC", "_Test Receivable USD - _TC", 100, save=False)
- jv.accounts[1].update({
- "party_type": "Customer",
- "party": "_Test Customer USD"
- })
+ jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
jv.submit()
# create jv in USD, but account currency in INR
- jv = make_journal_entry("_Test Bank - _TC",
- "_Test Receivable - _TC", 100, save=False)
+ jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False)
- jv.accounts[1].update({
- "party_type": "Customer",
- "party": "_Test Customer USD"
- })
+ jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
self.assertRaises(InvalidAccountCurrency, jv.submit)
# back in USD
- jv = make_journal_entry("_Test Bank USD - _TC",
- "_Test Receivable USD - _TC", 100, save=False)
+ jv = make_journal_entry("_Test Bank USD - _TC", "_Test Receivable USD - _TC", 100, save=False)
- jv.accounts[1].update({
- "party_type": "Customer",
- "party": "_Test Customer USD"
- })
+ jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
jv.submit()
@@ -245,13 +291,27 @@ def test_inter_company_jv(self):
frappe.db.set_value("Account", "Buildings - _TC", "inter_company_account", 1)
frappe.db.set_value("Account", "Sales Expenses - _TC1", "inter_company_account", 1)
frappe.db.set_value("Account", "Buildings - _TC1", "inter_company_account", 1)
- jv = make_journal_entry("Sales Expenses - _TC", "Buildings - _TC", 100, posting_date=nowdate(), cost_center = "Main - _TC", save=False)
+ jv = make_journal_entry(
+ "Sales Expenses - _TC",
+ "Buildings - _TC",
+ 100,
+ posting_date=nowdate(),
+ cost_center="Main - _TC",
+ save=False,
+ )
jv.voucher_type = "Inter Company Journal Entry"
jv.multi_currency = 0
jv.insert()
jv.submit()
- jv1 = make_journal_entry("Sales Expenses - _TC1", "Buildings - _TC1", 100, posting_date=nowdate(), cost_center = "Main - _TC1", save=False)
+ jv1 = make_journal_entry(
+ "Sales Expenses - _TC1",
+ "Buildings - _TC1",
+ 100,
+ posting_date=nowdate(),
+ cost_center="Main - _TC1",
+ save=False,
+ )
jv1.inter_company_journal_entry_reference = jv.name
jv1.company = "_Test Company 1"
jv1.voucher_type = "Inter Company Journal Entry"
@@ -273,9 +333,12 @@ def test_inter_company_jv(self):
def test_jv_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
- jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
+ jv = make_journal_entry(
+ "_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center=cost_center, save=False
+ )
jv.voucher_type = "Bank Entry"
jv.multi_currency = 0
jv.cheque_no = "112233"
@@ -284,17 +347,17 @@ def test_jv_with_cost_centre(self):
jv.submit()
expected_values = {
- "_Test Cash - _TC": {
- "cost_center": cost_center
- },
- "_Test Bank - _TC": {
- "cost_center": cost_center
- }
+ "_Test Cash - _TC": {"cost_center": cost_center},
+ "_Test Bank - _TC": {"cost_center": cost_center},
}
- gl_entries = frappe.db.sql("""select account, cost_center, debit, credit
+ gl_entries = frappe.db.sql(
+ """select account, cost_center, debit, credit
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""", jv.name, as_dict=1)
+ order by account asc""",
+ jv.name,
+ as_dict=1,
+ )
self.assertTrue(gl_entries)
@@ -305,11 +368,13 @@ def test_jv_with_project(self):
from erpnext.projects.doctype.project.test_project import make_project
if not frappe.db.exists("Project", {"project_name": "Journal Entry Project"}):
- project = make_project({
- 'project_name': 'Journal Entry Project',
- 'project_template_name': 'Test Project Template',
- 'start_date': '2020-01-01'
- })
+ project = make_project(
+ {
+ "project_name": "Journal Entry Project",
+ "project_template_name": "Test Project Template",
+ "start_date": "2020-01-01",
+ }
+ )
project_name = project.name
else:
project_name = frappe.get_value("Project", {"project_name": "_Test Project"})
@@ -325,17 +390,17 @@ def test_jv_with_project(self):
jv.submit()
expected_values = {
- "_Test Cash - _TC": {
- "project": project_name
- },
- "_Test Bank - _TC": {
- "project": project_name
- }
+ "_Test Cash - _TC": {"project": project_name},
+ "_Test Bank - _TC": {"project": project_name},
}
- gl_entries = frappe.db.sql("""select account, project, debit, credit
+ gl_entries = frappe.db.sql(
+ """select account, project, debit, credit
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
- order by account asc""", jv.name, as_dict=1)
+ order by account asc""",
+ jv.name,
+ as_dict=1,
+ )
self.assertTrue(gl_entries)
@@ -345,9 +410,12 @@ def test_jv_with_project(self):
def test_jv_account_and_party_balance_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_balance_on
+
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
- jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
+ jv = make_journal_entry(
+ "_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center=cost_center, save=False
+ )
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
jv.voucher_type = "Bank Entry"
jv.multi_currency = 0
@@ -360,7 +428,18 @@ def test_jv_account_and_party_balance_with_cost_centre(self):
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
self.assertEqual(expected_account_balance, account_balance)
-def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None):
+
+def make_journal_entry(
+ account1,
+ account2,
+ amount,
+ cost_center=None,
+ posting_date=None,
+ exchange_rate=1,
+ save=True,
+ submit=False,
+ project=None,
+):
if not cost_center:
cost_center = "_Test Cost Center - _TC"
@@ -369,23 +448,27 @@ def make_journal_entry(account1, account2, amount, cost_center=None, posting_dat
jv.company = "_Test Company"
jv.user_remark = "test"
jv.multi_currency = 1
- jv.set("accounts", [
- {
- "account": account1,
- "cost_center": cost_center,
- "project": project,
- "debit_in_account_currency": amount if amount > 0 else 0,
- "credit_in_account_currency": abs(amount) if amount < 0 else 0,
- "exchange_rate": exchange_rate
- }, {
- "account": account2,
- "cost_center": cost_center,
- "project": project,
- "credit_in_account_currency": amount if amount > 0 else 0,
- "debit_in_account_currency": abs(amount) if amount < 0 else 0,
- "exchange_rate": exchange_rate
- }
- ])
+ jv.set(
+ "accounts",
+ [
+ {
+ "account": account1,
+ "cost_center": cost_center,
+ "project": project,
+ "debit_in_account_currency": amount if amount > 0 else 0,
+ "credit_in_account_currency": abs(amount) if amount < 0 else 0,
+ "exchange_rate": exchange_rate,
+ },
+ {
+ "account": account2,
+ "cost_center": cost_center,
+ "project": project,
+ "credit_in_account_currency": amount if amount > 0 else 0,
+ "debit_in_account_currency": abs(amount) if amount < 0 else 0,
+ "exchange_rate": exchange_rate,
+ },
+ ],
+ )
if save or submit:
jv.insert()
@@ -394,4 +477,5 @@ def make_journal_entry(account1, account2, amount, cost_center=None, posting_dat
return jv
-test_records = frappe.get_test_records('Journal Entry')
+
+test_records = frappe.get_test_records("Journal Entry")
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
index 2da72c20ad81..b8ef3545d331 100644
--- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
@@ -9,6 +9,7 @@
class JournalEntryTemplate(Document):
pass
+
@frappe.whitelist()
def get_naming_series():
return frappe.get_meta("Journal Entry").get_field("naming_series").options
diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py
index 830ad370d704..c1e2975fbc95 100644
--- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py
+++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py
@@ -15,9 +15,7 @@ def start_merge(self):
from frappe.utils.scheduler import is_scheduler_inactive
if is_scheduler_inactive() and not frappe.flags.in_test:
- frappe.throw(
- _("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive")
- )
+ frappe.throw(_("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive"))
enqueued_jobs = [d.get("job_name") for d in get_info()]
@@ -35,10 +33,12 @@ def start_merge(self):
return False
+
@frappe.whitelist()
def form_start_merge(docname):
return frappe.get_doc("Ledger Merge", docname).start_merge()
+
def start_merge(docname):
ledger_merge = frappe.get_doc("Ledger Merge", docname)
successful_merges = 0
@@ -51,26 +51,24 @@ def start_merge(docname):
ledger_merge.account,
ledger_merge.is_group,
ledger_merge.root_type,
- ledger_merge.company
+ ledger_merge.company,
)
- row.db_set('merged', 1)
+ row.db_set("merged", 1)
frappe.db.commit()
successful_merges += 1
- frappe.publish_realtime("ledger_merge_progress", {
- "ledger_merge": ledger_merge.name,
- "current": successful_merges,
- "total": total
- }
+ frappe.publish_realtime(
+ "ledger_merge_progress",
+ {"ledger_merge": ledger_merge.name, "current": successful_merges, "total": total},
)
except Exception:
frappe.db.rollback()
frappe.log_error(title=ledger_merge.name)
finally:
if successful_merges == total:
- ledger_merge.db_set('status', 'Success')
+ ledger_merge.db_set("status", "Success")
elif successful_merges > 0:
- ledger_merge.db_set('status', 'Partial Success')
+ ledger_merge.db_set("status", "Partial Success")
else:
- ledger_merge.db_set('status', 'Error')
+ ledger_merge.db_set("status", "Error")
frappe.publish_realtime("ledger_merge_refresh", {"ledger_merge": ledger_merge.name})
diff --git a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py
index f7315362b7bf..992ce9ede5dd 100644
--- a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py
+++ b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py
@@ -31,18 +31,17 @@ def test_merge_success(self):
acc.company = "_Test Company"
acc.insert()
- doc = frappe.get_doc({
- "doctype": "Ledger Merge",
- "company": "_Test Company",
- "root_type": frappe.db.get_value("Account", "Indirect Test Expenses - _TC", "root_type"),
- "account": "Indirect Expenses - _TC",
- "merge_accounts": [
- {
- "account": "Indirect Test Expenses - _TC",
- "account_name": "Indirect Expenses"
- }
- ]
- }).insert(ignore_permissions=True)
+ doc = frappe.get_doc(
+ {
+ "doctype": "Ledger Merge",
+ "company": "_Test Company",
+ "root_type": frappe.db.get_value("Account", "Indirect Test Expenses - _TC", "root_type"),
+ "account": "Indirect Expenses - _TC",
+ "merge_accounts": [
+ {"account": "Indirect Test Expenses - _TC", "account_name": "Indirect Expenses"}
+ ],
+ }
+ ).insert(ignore_permissions=True)
parent = frappe.db.get_value("Account", "Administrative Test Expenses - _TC", "parent_account")
self.assertEqual(parent, "Indirect Test Expenses - _TC")
@@ -76,22 +75,18 @@ def test_partial_merge_success(self):
acc.company = "_Test Company"
acc.insert()
- doc = frappe.get_doc({
- "doctype": "Ledger Merge",
- "company": "_Test Company",
- "root_type": frappe.db.get_value("Account", "Indirect Income - _TC", "root_type"),
- "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"
- }
- ]
- }).insert(ignore_permissions=True)
+ doc = frappe.get_doc(
+ {
+ "doctype": "Ledger Merge",
+ "company": "_Test Company",
+ "root_type": frappe.db.get_value("Account", "Indirect Income - _TC", "root_type"),
+ "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"},
+ ],
+ }
+ ).insert(ignore_permissions=True)
parent = frappe.db.get_value("Account", "Administrative Test Income - _TC", "parent_account")
self.assertEqual(parent, "Indirect Test Income - _TC")
@@ -112,7 +107,7 @@ def tearDown(self):
"Indirect Test Expenses - _TC",
"Administrative Test Expenses - _TC",
"Indirect Test Income - _TC",
- "Administrative Test Income - _TC"
+ "Administrative Test Income - _TC",
]
for account in test_accounts:
frappe.delete_doc_if_exists("Account", account)
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
index f460b9f79532..dcb43fb2cb30 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
@@ -8,6 +8,7 @@
exclude_from_linked_with = True
+
class LoyaltyPointEntry(Document):
pass
@@ -16,18 +17,28 @@ def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=No
if not expiry_date:
expiry_date = today()
- return frappe.db.sql('''
+ return frappe.db.sql(
+ """
select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
from `tabLoyalty Point Entry`
where customer=%s and loyalty_program=%s
and expiry_date>=%s and loyalty_points>0 and company=%s
order by expiry_date
- ''', (customer, loyalty_program, expiry_date, company), as_dict=1)
+ """,
+ (customer, loyalty_program, expiry_date, company),
+ as_dict=1,
+ )
+
def get_redemption_details(customer, loyalty_program, company):
- return frappe._dict(frappe.db.sql('''
+ return frappe._dict(
+ frappe.db.sql(
+ """
select redeem_against, sum(loyalty_points)
from `tabLoyalty Point Entry`
where customer=%s and loyalty_program=%s and loyalty_points<0 and company=%s
group by redeem_against
- ''', (customer, loyalty_program, company)))
+ """,
+ (customer, loyalty_program, company),
+ )
+ )
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
index 70da03b27f3e..48a25ad6b81e 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
@@ -12,39 +12,61 @@ class LoyaltyProgram(Document):
pass
-def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False):
+def get_loyalty_details(
+ customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False
+):
if not expiry_date:
expiry_date = today()
- condition = ''
+ condition = ""
if company:
condition = " and company=%s " % frappe.db.escape(company)
if not include_expired_entry:
condition += " and expiry_date>='%s' " % expiry_date
- loyalty_point_details = frappe.db.sql('''select sum(loyalty_points) as loyalty_points,
+ loyalty_point_details = frappe.db.sql(
+ """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),
- (customer, loyalty_program, expiry_date), as_dict=1)
+ group by customer""".format(
+ condition=condition
+ ),
+ (customer, loyalty_program, expiry_date),
+ as_dict=1,
+ )
if loyalty_point_details:
return loyalty_point_details[0]
else:
return {"loyalty_points": 0, "total_spent": 0}
+
@frappe.whitelist()
-def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, \
- silent=False, include_expired_entry=False, current_transaction_amount=0):
- lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
+def get_loyalty_program_details_with_points(
+ customer,
+ loyalty_program=None,
+ expiry_date=None,
+ company=None,
+ silent=False,
+ include_expired_entry=False,
+ current_transaction_amount=0,
+):
+ 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))
-
- tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
- key=lambda rule:rule.min_spent, reverse=True)
+ lp_details.update(
+ get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)
+ )
+
+ tier_spent_level = sorted(
+ [d.as_dict() for d in loyalty_program.collection_rules],
+ key=lambda rule: rule.min_spent,
+ reverse=True,
+ )
for i, d in enumerate(tier_spent_level):
- if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent:
+ if i == 0 or (lp_details.total_spent + current_transaction_amount) <= d.min_spent:
lp_details.tier_name = d.tier_name
lp_details.collection_factor = d.collection_factor
else:
@@ -52,8 +74,16 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi
return lp_details
+
@frappe.whitelist()
-def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False):
+def get_loyalty_program_details(
+ customer,
+ loyalty_program=None,
+ expiry_date=None,
+ company=None,
+ silent=False,
+ include_expired_entry=False,
+):
lp_details = frappe._dict()
if not loyalty_program:
@@ -72,6 +102,7 @@ def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None
lp_details.update(loyalty_program.as_dict())
return lp_details
+
@frappe.whitelist()
def get_redeemption_factor(loyalty_program=None, customer=None):
customer_loyalty_program = None
@@ -98,13 +129,16 @@ def validate_loyalty_points(ref_doc, points_to_redeem):
else:
loyalty_program = frappe.db.get_value("Customer", ref_doc.customer, ["loyalty_program"])
- if loyalty_program and frappe.db.get_value("Loyalty Program", loyalty_program, ["company"]) !=\
- ref_doc.company:
+ if (
+ loyalty_program
+ and frappe.db.get_value("Loyalty Program", loyalty_program, ["company"]) != ref_doc.company
+ ):
frappe.throw(_("The Loyalty Program isn't valid for the selected company"))
if loyalty_program and points_to_redeem:
- loyalty_program_details = get_loyalty_program_details_with_points(ref_doc.customer, loyalty_program,
- posting_date, ref_doc.company)
+ loyalty_program_details = get_loyalty_program_details_with_points(
+ ref_doc.customer, loyalty_program, posting_date, ref_doc.company
+ )
if points_to_redeem > loyalty_program_details.loyalty_points:
frappe.throw(_("You don't have enought Loyalty Points to redeem"))
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
index 25328e501eeb..3a4f908a1e9f 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
@@ -1,9 +1,5 @@
def get_data():
return {
- 'fieldname': 'loyalty_program',
- 'transactions': [
- {
- 'items': ['Sales Invoice', 'Customer']
- }
- ]
+ "fieldname": "loyalty_program",
+ "transactions": [{"items": ["Sales Invoice", "Customer"]}],
}
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
index 82c14324f5c6..3641ac4428f1 100644
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -19,19 +19,28 @@ def setUpClass(self):
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()
si_original.submit()
- customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
+ customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
earned_points = get_points_earned(si_original)
- lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
-
- self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
- self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
+ lpe = frappe.get_doc(
+ "Loyalty Point Entry",
+ {
+ "invoice_type": "Sales Invoice",
+ "invoice": si_original.name,
+ "customer": si_original.customer,
+ },
+ )
+
+ self.assertEqual(si_original.get("loyalty_program"), customer.loyalty_program)
+ self.assertEqual(lpe.get("loyalty_program_tier"), customer.loyalty_program_tier)
self.assertEqual(lpe.loyalty_points, earned_points)
# add redemption point
@@ -43,21 +52,31 @@ def test_loyalty_points_earned_single_tier(self):
earned_after_redemption = get_points_earned(si_redeem)
- lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name})
- lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]})
+ lpe_redeem = frappe.get_doc(
+ "Loyalty Point Entry",
+ {"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "redeem_against": lpe.name},
+ )
+ lpe_earn = frappe.get_doc(
+ "Loyalty Point Entry",
+ {"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "name": ["!=", lpe_redeem.name]},
+ )
self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption)
- self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points))
+ self.assertEqual(lpe_redeem.loyalty_points, (-1 * earned_points))
# cancel and delete
for d in [si_redeem, si_original]:
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('Loyalty Program', {'loyalty_program_name': 'Test Multiple Loyalty'}).name
+ customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
+ customer.loyalty_program = frappe.get_doc(
+ "Loyalty Program", {"loyalty_program_name": "Test Multiple Loyalty"}
+ ).name
customer.save()
# create a new sales invoice
@@ -67,10 +86,17 @@ def test_loyalty_points_earned_multiple_tier(self):
earned_points = get_points_earned(si_original)
- lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
-
- self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
- self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
+ lpe = frappe.get_doc(
+ "Loyalty Point Entry",
+ {
+ "invoice_type": "Sales Invoice",
+ "invoice": si_original.name,
+ "customer": si_original.customer,
+ },
+ )
+
+ self.assertEqual(si_original.get("loyalty_program"), customer.loyalty_program)
+ self.assertEqual(lpe.get("loyalty_program_tier"), customer.loyalty_program_tier)
self.assertEqual(lpe.loyalty_points, earned_points)
# add redemption point
@@ -80,14 +106,20 @@ def test_loyalty_points_earned_multiple_tier(self):
si_redeem.insert()
si_redeem.submit()
- customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
+ customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
earned_after_redemption = get_points_earned(si_redeem)
- lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name})
- lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]})
+ lpe_redeem = frappe.get_doc(
+ "Loyalty Point Entry",
+ {"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "redeem_against": lpe.name},
+ )
+ lpe_earn = frappe.get_doc(
+ "Loyalty Point Entry",
+ {"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "name": ["!=", lpe_redeem.name]},
+ )
self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption)
- self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points))
+ self.assertEqual(lpe_redeem.loyalty_points, (-1 * earned_points))
self.assertEqual(lpe_earn.loyalty_program_tier, customer.loyalty_program_tier)
# cancel and delete
@@ -95,23 +127,30 @@ def test_loyalty_points_earned_multiple_tier(self):
d.cancel()
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")
+ """cancelling the sales invoice should cancel the earned points"""
+ 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()
si.submit()
- lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si.name, 'customer': si.customer})
+ lpe = frappe.get_doc(
+ "Loyalty Point Entry",
+ {"invoice_type": "Sales Invoice", "invoice": si.name, "customer": si.customer},
+ )
self.assertEqual(True, not (lpe is None))
# cancelling sales invoice
si.cancel()
- lpe = frappe.db.exists('Loyalty Point Entry', lpe.name)
+ lpe = frappe.db.exists("Loyalty Point Entry", lpe.name)
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)
@@ -119,7 +158,14 @@ def test_sales_invoice_return(self):
si_original.submit()
earned_points = get_points_earned(si_original)
- lpe_original = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
+ lpe_original = frappe.get_doc(
+ "Loyalty Point Entry",
+ {
+ "invoice_type": "Sales Invoice",
+ "invoice": si_original.name,
+ "customer": si_original.customer,
+ },
+ )
self.assertEqual(lpe_original.loyalty_points, earned_points)
# create sales invoice return
@@ -131,10 +177,17 @@ def test_sales_invoice_return(self):
si_return.submit()
# fetch original invoice again as its status would have been updated
- si_original = frappe.get_doc('Sales Invoice', lpe_original.invoice)
+ si_original = frappe.get_doc("Sales Invoice", lpe_original.invoice)
earned_points = get_points_earned(si_original)
- lpe_after_return = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
+ lpe_after_return = frappe.get_doc(
+ "Loyalty Point Entry",
+ {
+ "invoice_type": "Sales Invoice",
+ "invoice": si_original.name,
+ "customer": si_original.customer,
+ },
+ )
self.assertEqual(lpe_after_return.loyalty_points, earned_points)
self.assertEqual(True, (lpe_original.loyalty_points > lpe_after_return.loyalty_points))
@@ -143,144 +196,164 @@ def test_sales_invoice_return(self):
try:
d.cancel()
except frappe.TimestampMismatchError:
- frappe.get_doc('Sales Invoice', d.name).cancel()
+ frappe.get_doc("Sales Invoice", d.name).cancel()
def test_loyalty_points_for_dashboard(self):
- doc = frappe.get_doc('Customer', 'Test Loyalty Customer')
+ doc = frappe.get_doc("Customer", "Test Loyalty Customer")
company_wise_info = get_dashboard_info("Customer", doc.name, doc.loyalty_program)
for d in company_wise_info:
self.assertTrue(d.get("loyalty_points"))
+
def get_points_earned(self):
def get_returned_amount():
- returned_amount = frappe.db.sql("""
+ returned_amount = frappe.db.sql(
+ """
select sum(grand_total)
from `tabSales Invoice`
where docstatus=1 and is_return=1 and ifnull(return_against, '')=%s
- """, self.name)
+ """,
+ self.name,
+ )
return abs(flt(returned_amount[0][0])) if returned_amount else 0
- lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
- loyalty_program=self.loyalty_program, expiry_date=self.posting_date, include_expired_entry=True)
- if lp_details 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)):
+ lp_details = get_loyalty_program_details_with_points(
+ self.customer,
+ company=self.company,
+ loyalty_program=self.loyalty_program,
+ expiry_date=self.posting_date,
+ include_expired_entry=True,
+ )
+ if (
+ lp_details
+ 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))
+ ):
returned_amount = get_returned_amount()
eligible_amount = flt(self.grand_total) - cint(self.loyalty_amount) - returned_amount
- points_earned = cint(eligible_amount/lp_details.collection_factor)
+ points_earned = cint(eligible_amount / lp_details.collection_factor)
return points_earned or 0
+
def create_sales_invoice_record(qty=1):
# return sales invoice doc object
- return frappe.get_doc({
- "doctype": "Sales Invoice",
- "customer": frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"}).name,
- "company": '_Test Company',
- "due_date": today(),
- "posting_date": today(),
- "currency": "INR",
- "taxes_and_charges": "",
- "debit_to": "Debtors - _TC",
- "taxes": [],
- "items": [{
- 'doctype': 'Sales Invoice Item',
- 'item_code': frappe.get_doc('Item', {'item_name': 'Loyal Item'}).name,
- 'qty': qty,
- "rate": 10000,
- 'income_account': 'Sales - _TC',
- 'cost_center': 'Main - _TC',
- 'expense_account': 'Cost of Goods Sold - _TC'
- }]
- })
+ return frappe.get_doc(
+ {
+ "doctype": "Sales Invoice",
+ "customer": frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"}).name,
+ "company": "_Test Company",
+ "due_date": today(),
+ "posting_date": today(),
+ "currency": "INR",
+ "taxes_and_charges": "",
+ "debit_to": "Debtors - _TC",
+ "taxes": [],
+ "items": [
+ {
+ "doctype": "Sales Invoice Item",
+ "item_code": frappe.get_doc("Item", {"item_name": "Loyal Item"}).name,
+ "qty": qty,
+ "rate": 10000,
+ "income_account": "Sales - _TC",
+ "cost_center": "Main - _TC",
+ "expense_account": "Cost of Goods Sold - _TC",
+ }
+ ],
+ }
+ )
+
def create_records():
# create a new loyalty Account
if not frappe.db.exists("Account", "Loyalty - _TC"):
- frappe.get_doc({
- "doctype": "Account",
- "account_name": "Loyalty",
- "parent_account": "Direct Expenses - _TC",
- "company": "_Test Company",
- "is_group": 0,
- "account_type": "Expense Account",
- }).insert()
+ frappe.get_doc(
+ {
+ "doctype": "Account",
+ "account_name": "Loyalty",
+ "parent_account": "Direct Expenses - _TC",
+ "company": "_Test Company",
+ "is_group": 0,
+ "account_type": "Expense Account",
+ }
+ ).insert()
# create a new loyalty program Single tier
- if not frappe.db.exists("Loyalty Program","Test Single Loyalty"):
- frappe.get_doc({
- "doctype": "Loyalty Program",
- "loyalty_program_name": "Test Single Loyalty",
- "auto_opt_in": 1,
- "from_date": today(),
- "loyalty_program_type": "Single Tier Program",
- "conversion_factor": 1,
- "expiry_duration": 10,
- "company": "_Test Company",
- "cost_center": "Main - _TC",
- "expense_account": "Loyalty - _TC",
- "collection_rules": [{
- 'tier_name': 'Silver',
- 'collection_factor': 1000,
- 'min_spent': 1000
- }]
- }).insert()
+ if not frappe.db.exists("Loyalty Program", "Test Single Loyalty"):
+ frappe.get_doc(
+ {
+ "doctype": "Loyalty Program",
+ "loyalty_program_name": "Test Single Loyalty",
+ "auto_opt_in": 1,
+ "from_date": today(),
+ "loyalty_program_type": "Single Tier Program",
+ "conversion_factor": 1,
+ "expiry_duration": 10,
+ "company": "_Test Company",
+ "cost_center": "Main - _TC",
+ "expense_account": "Loyalty - _TC",
+ "collection_rules": [{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 1000}],
+ }
+ ).insert()
# create a new customer
- if not frappe.db.exists("Customer","Test Loyalty Customer"):
- frappe.get_doc({
- "customer_group": "_Test Customer Group",
- "customer_name": "Test Loyalty Customer",
- "customer_type": "Individual",
- "doctype": "Customer",
- "territory": "_Test Territory"
- }).insert()
+ if not frappe.db.exists("Customer", "Test Loyalty Customer"):
+ frappe.get_doc(
+ {
+ "customer_group": "_Test Customer Group",
+ "customer_name": "Test Loyalty Customer",
+ "customer_type": "Individual",
+ "doctype": "Customer",
+ "territory": "_Test Territory",
+ }
+ ).insert()
# create a new loyalty program Multiple tier
- if not frappe.db.exists("Loyalty Program","Test Multiple Loyalty"):
- frappe.get_doc({
- "doctype": "Loyalty Program",
- "loyalty_program_name": "Test Multiple Loyalty",
- "auto_opt_in": 1,
- "from_date": today(),
- "loyalty_program_type": "Multiple Tier Program",
- "conversion_factor": 1,
- "expiry_duration": 10,
- "company": "_Test Company",
- "cost_center": "Main - _TC",
- "expense_account": "Loyalty - _TC",
- "collection_rules": [
- {
- 'tier_name': 'Silver',
- 'collection_factor': 1000,
- 'min_spent': 10000
- },
- {
- 'tier_name': 'Gold',
- 'collection_factor': 1000,
- 'min_spent': 19000
- }
- ]
- }).insert()
+ if not frappe.db.exists("Loyalty Program", "Test Multiple Loyalty"):
+ frappe.get_doc(
+ {
+ "doctype": "Loyalty Program",
+ "loyalty_program_name": "Test Multiple Loyalty",
+ "auto_opt_in": 1,
+ "from_date": today(),
+ "loyalty_program_type": "Multiple Tier Program",
+ "conversion_factor": 1,
+ "expiry_duration": 10,
+ "company": "_Test Company",
+ "cost_center": "Main - _TC",
+ "expense_account": "Loyalty - _TC",
+ "collection_rules": [
+ {"tier_name": "Silver", "collection_factor": 1000, "min_spent": 10000},
+ {"tier_name": "Gold", "collection_factor": 1000, "min_spent": 19000},
+ ],
+ }
+ ).insert()
# create an item
if not frappe.db.exists("Item", "Loyal Item"):
- frappe.get_doc({
- "doctype": "Item",
- "item_code": "Loyal Item",
- "item_name": "Loyal Item",
- "item_group": "All Item Groups",
- "company": "_Test Company",
- "is_stock_item": 1,
- "opening_stock": 100,
- "valuation_rate": 10000,
- }).insert()
+ frappe.get_doc(
+ {
+ "doctype": "Item",
+ "item_code": "Loyal Item",
+ "item_name": "Loyal Item",
+ "item_group": "All Item Groups",
+ "company": "_Test Company",
+ "is_stock_item": 1,
+ "opening_stock": 100,
+ "valuation_rate": 10000,
+ }
+ ).insert()
# create item price
- if not frappe.db.exists("Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}):
- frappe.get_doc({
- "doctype": "Item Price",
- "price_list": "Standard Selling",
- "item_code": "Loyal Item",
- "price_list_rate": 10000
- }).insert()
+ if not frappe.db.exists(
+ "Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}
+ ):
+ frappe.get_doc(
+ {
+ "doctype": "Item Price",
+ "price_list": "Standard Selling",
+ "item_code": "Loyal Item",
+ "price_list_rate": 10000,
+ }
+ ).insert()
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
index f21d1b9baa9d..d0373021a69e 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
@@ -19,23 +19,35 @@ def validate_repeating_companies(self):
for entry in self.accounts:
accounts_list.append(entry.company)
- if len(accounts_list)!= len(set(accounts_list)):
+ if len(accounts_list) != len(set(accounts_list)):
frappe.throw(_("Same Company is entered more than once"))
def validate_accounts(self):
for entry in self.accounts:
"""Error when Company of Ledger account doesn't match with Company Selected"""
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
- frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
- .format(entry.default_account, entry.company, self.name))
+ frappe.throw(
+ _("Account {0} does not match with Company {1} in Mode of Account: {2}").format(
+ entry.default_account, entry.company, self.name
+ )
+ )
def validate_pos_mode_of_payment(self):
if not self.enabled:
- pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip
- WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""", (self.name))
+ pos_profiles = frappe.db.sql(
+ """SELECT sip.parent FROM `tabSales Invoice Payment` sip
+ WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""",
+ (self.name),
+ )
pos_profiles = list(map(lambda x: x[0], pos_profiles))
if pos_profiles:
- message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
- Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
+ message = (
+ "POS Profile "
+ + frappe.bold(", ".join(pos_profiles))
+ + " contains \
+ Mode of Payment "
+ + frappe.bold(str(self.name))
+ + ". Please remove them to disable this mode."
+ )
frappe.throw(_(message), title="Not Allowed")
diff --git a/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py
index 2ff02a7c4dce..9733fb89e2f2 100644
--- a/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py
+++ b/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py
@@ -5,5 +5,6 @@
# test_records = frappe.get_test_records('Mode of Payment')
+
class TestModeofPayment(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
index a8c5f68c110f..1d19708eddfc 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
@@ -11,13 +11,25 @@
class MonthlyDistribution(Document):
@frappe.whitelist()
def get_months(self):
- month_list = ['January','February','March','April','May','June','July','August','September',
- 'October','November','December']
- idx =1
+ month_list = [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ ]
+ idx = 1
for m in month_list:
- mnth = self.append('percentages')
+ mnth = self.append("percentages")
mnth.month = m
- mnth.percentage_allocation = 100.0/12
+ mnth.percentage_allocation = 100.0 / 12
mnth.idx = idx
idx += 1
@@ -25,18 +37,15 @@ def validate(self):
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%") + " ({0}%)".format(str(flt(total, 2)))
+ )
+
def get_periodwise_distribution_data(distribution_id, period_list, periodicity):
- doc = frappe.get_doc('Monthly Distribution', distribution_id)
+ doc = frappe.get_doc("Monthly Distribution", distribution_id)
- months_to_add = {
- "Yearly": 12,
- "Half-Yearly": 6,
- "Quarterly": 3,
- "Monthly": 1
- }[periodicity]
+ months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
period_dict = {}
@@ -45,6 +54,7 @@ def get_periodwise_distribution_data(distribution_id, period_list, periodicity):
return period_dict
+
def get_percentage(doc, start_date, period):
percentage = 0
months = [start_date.strftime("%B").title()]
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
index 96008c47339c..ba2cb671d40b 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
@@ -3,19 +3,14 @@
def get_data():
return {
- 'fieldname': 'monthly_distribution',
- 'non_standard_fieldnames': {
- 'Sales Person': 'distribution_id',
- 'Territory': 'distribution_id',
- 'Sales Partner': 'distribution_id',
+ "fieldname": "monthly_distribution",
+ "non_standard_fieldnames": {
+ "Sales Person": "distribution_id",
+ "Territory": "distribution_id",
+ "Sales Partner": "distribution_id",
},
- 'transactions': [
- {
- 'label': _('Target Details'),
- 'items': ['Sales Person', 'Territory', 'Sales Partner']
- },
- {
- 'items': ['Budget']
- }
- ]
+ "transactions": [
+ {"label": _("Target Details"), "items": ["Sales Person", "Territory", "Sales Partner"]},
+ {"items": ["Budget"]},
+ ],
}
diff --git a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
index 4a878b2aaf7c..848b1f9de761 100644
--- a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
@@ -6,7 +6,8 @@
import frappe
-test_records = frappe.get_test_records('Monthly Distribution')
+test_records = frappe.get_test_records("Monthly Distribution")
+
class TestMonthlyDistribution(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
index 6e7b80e7310d..9d4d76b8f633 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
@@ -20,9 +20,9 @@ class OpeningInvoiceCreationTool(Document):
def onload(self):
"""Load the Opening Invoice summary"""
summary, max_count = self.get_opening_invoice_summary()
- self.set_onload('opening_invoices_summary', summary)
- self.set_onload('max_count', max_count)
- self.set_onload('temporary_opening_account', get_temporary_opening_account(self.company))
+ self.set_onload("opening_invoices_summary", summary)
+ self.set_onload("max_count", max_count)
+ self.set_onload("temporary_opening_account", get_temporary_opening_account(self.company))
def get_opening_invoice_summary(self):
def prepare_invoice_summary(doctype, invoices):
@@ -32,10 +32,7 @@ def prepare_invoice_summary(doctype, invoices):
for invoice in invoices:
company = invoice.pop("company")
_summary = invoices_summary.get(company, {})
- _summary.update({
- "currency": company_wise_currency.get(company),
- doctype: invoice
- })
+ _summary.update({"currency": company_wise_currency.get(company), doctype: invoice})
invoices_summary.update({company: _summary})
if invoice.paid_amount:
@@ -44,17 +41,21 @@ def prepare_invoice_summary(doctype, invoices):
outstanding_amount.append(invoice.outstanding_amount)
if paid_amount or outstanding_amount:
- max_count.update({
- doctype: {
- "max_paid": max(paid_amount) if paid_amount else 0.0,
- "max_due": max(outstanding_amount) if outstanding_amount else 0.0
+ max_count.update(
+ {
+ doctype: {
+ "max_paid": max(paid_amount) if paid_amount else 0.0,
+ "max_due": max(outstanding_amount) if outstanding_amount else 0.0,
+ }
}
- })
+ )
invoices_summary = {}
max_count = {}
fields = [
- "company", "count(name) as total_invoices", "sum(outstanding_amount) as outstanding_amount"
+ "company",
+ "count(name) as total_invoices",
+ "sum(outstanding_amount) as outstanding_amount",
]
companies = frappe.get_all("Company", fields=["name as company", "default_currency as currency"])
if not companies:
@@ -62,8 +63,9 @@ def prepare_invoice_summary(doctype, invoices):
company_wise_currency = {row.company: row.currency for row in companies}
for doctype in ["Sales Invoice", "Purchase Invoice"]:
- invoices = frappe.get_all(doctype, filters=dict(is_opening="Yes", docstatus=1),
- fields=fields, group_by="company")
+ invoices = frappe.get_all(
+ doctype, filters=dict(is_opening="Yes", docstatus=1), fields=fields, group_by="company"
+ )
prepare_invoice_summary(doctype, invoices)
return invoices_summary, max_count
@@ -74,7 +76,9 @@ def validate_company(self):
def set_missing_values(self, row):
row.qty = row.qty or 1.0
- row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(self.company)
+ row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(
+ self.company
+ )
row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier"
row.item_name = row.item_name or _("Opening Invoice Item")
row.posting_date = row.posting_date or nowdate()
@@ -85,7 +89,11 @@ def validate_mandatory_invoice_fields(self, row):
if self.create_missing_party:
self.add_party(row.party_type, row.party)
else:
- frappe.throw(_("Row #{}: {} {} does not exist.").format(row.idx, frappe.bold(row.party_type), frappe.bold(row.party)))
+ frappe.throw(
+ _("Row #{}: {} {} does not exist.").format(
+ row.idx, frappe.bold(row.party_type), frappe.bold(row.party)
+ )
+ )
mandatory_error_msg = _("Row #{0}: {1} is required to create the Opening {2} Invoices")
for d in ("Party", "Outstanding Amount", "Temporary Opening Account"):
@@ -100,12 +108,19 @@ def get_invoices(self):
self.set_missing_values(row)
self.validate_mandatory_invoice_fields(row)
invoice = self.get_invoice_dict(row)
- company_details = frappe.get_cached_value('Company', self.company, ["default_currency", "default_letter_head"], as_dict=1) or {}
+ company_details = (
+ frappe.get_cached_value(
+ "Company", self.company, ["default_currency", "default_letter_head"], as_dict=1
+ )
+ or {}
+ )
if company_details:
- invoice.update({
- "currency": company_details.get("default_currency"),
- "letter_head": company_details.get("default_letter_head")
- })
+ invoice.update(
+ {
+ "currency": company_details.get("default_currency"),
+ "letter_head": company_details.get("default_letter_head"),
+ }
+ )
invoices.append(invoice)
return invoices
@@ -127,55 +142,61 @@ def add_party(self, party_type, party):
def get_invoice_dict(self, row=None):
def get_item_dict():
- cost_center = row.get('cost_center') or frappe.get_cached_value('Company', self.company, "cost_center")
+ cost_center = row.get("cost_center") or frappe.get_cached_value(
+ "Company", self.company, "cost_center"
+ )
if not cost_center:
- frappe.throw(_("Please set the Default Cost Center in {0} company.").format(frappe.bold(self.company)))
+ frappe.throw(
+ _("Please set the Default Cost Center in {0} company.").format(frappe.bold(self.company))
+ )
- income_expense_account_field = "income_account" if row.party_type == "Customer" else "expense_account"
+ income_expense_account_field = (
+ "income_account" if row.party_type == "Customer" else "expense_account"
+ )
default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos")
rate = flt(row.outstanding_amount) / flt(row.qty)
- item_dict = frappe._dict({
- "uom": default_uom,
- "rate": rate or 0.0,
- "qty": row.qty,
- "conversion_factor": 1.0,
- "item_name": row.item_name or "Opening Invoice Item",
- "description": row.item_name or "Opening Invoice Item",
- income_expense_account_field: row.temporary_opening_account,
- "cost_center": cost_center
- })
+ item_dict = frappe._dict(
+ {
+ "uom": default_uom,
+ "rate": rate or 0.0,
+ "qty": row.qty,
+ "conversion_factor": 1.0,
+ "item_name": row.item_name or "Opening Invoice Item",
+ "description": row.item_name or "Opening Invoice Item",
+ income_expense_account_field: row.temporary_opening_account,
+ "cost_center": cost_center,
+ }
+ )
for dimension in get_accounting_dimensions():
- item_dict.update({
- dimension: row.get(dimension)
- })
+ item_dict.update({dimension: row.get(dimension)})
return item_dict
item = get_item_dict()
- invoice = frappe._dict({
- "items": [item],
- "is_opening": "Yes",
- "set_posting_time": 1,
- "company": self.company,
- "cost_center": self.cost_center,
- "due_date": row.due_date,
- "posting_date": row.posting_date,
- frappe.scrub(row.party_type): row.party,
- "is_pos": 0,
- "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
- "update_stock": 0, # important: https://github.com/frappe/erpnext/pull/23559
- "invoice_number": row.invoice_number,
- "disable_rounded_total": 1
- })
+ invoice = frappe._dict(
+ {
+ "items": [item],
+ "is_opening": "Yes",
+ "set_posting_time": 1,
+ "company": self.company,
+ "cost_center": self.cost_center,
+ "due_date": row.due_date,
+ "posting_date": row.posting_date,
+ frappe.scrub(row.party_type): row.party,
+ "is_pos": 0,
+ "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
+ "update_stock": 0, # important: https://github.com/frappe/erpnext/pull/23559
+ "invoice_number": row.invoice_number,
+ "disable_rounded_total": 1,
+ }
+ )
accounting_dimension = get_accounting_dimensions()
for dimension in accounting_dimension:
- invoice.update({
- dimension: self.get(dimension) or item.get(dimension)
- })
+ invoice.update({dimension: self.get(dimension) or item.get(dimension)})
return invoice
@@ -201,9 +222,10 @@ def make_invoices(self):
event="opening_invoice_creation",
job_name=self.name,
invoices=invoices,
- now=frappe.conf.developer_mode or frappe.flags.in_test
+ now=frappe.conf.developer_mode or frappe.flags.in_test,
)
+
def start_import(invoices):
errors = 0
names = []
@@ -222,14 +244,22 @@ def start_import(invoices):
except Exception:
errors += 1
frappe.db.rollback()
- message = "\n".join(["Data:", dumps(d, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
+ message = "\n".join(
+ ["Data:", dumps(d, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]
+ )
frappe.log_error(title="Error while creating Opening Invoice", message=message)
frappe.db.commit()
if errors:
- frappe.msgprint(_("You had {} errors while creating opening invoices. Check {} for more details")
- .format(errors, "Error Log"), indicator="red", title=_("Error Occured"))
+ frappe.msgprint(
+ _("You had {} errors while creating opening invoices. Check {} for more details").format(
+ errors, "Error Log"
+ ),
+ indicator="red",
+ title=_("Error Occured"),
+ )
return names
+
def publish(index, total, doctype):
if total < 50:
return
@@ -237,21 +267,20 @@ def publish(index, total, doctype):
"opening_invoice_creation_progress",
dict(
title=_("Opening Invoice Creation In Progress"),
- message=_('Creating {} out of {} {}').format(index + 1, total, doctype),
+ message=_("Creating {} out of {} {}").format(index + 1, total, doctype),
user=frappe.session.user,
- count=index+1,
- total=total
- ))
+ count=index + 1,
+ total=total,
+ ),
+ )
+
@frappe.whitelist()
def get_temporary_opening_account(company=None):
if not company:
return
- accounts = frappe.get_all("Account", filters={
- 'company': company,
- 'account_type': 'Temporary'
- })
+ accounts = frappe.get_all("Account", filters={"company": company, "account_type": "Temporary"})
if not accounts:
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
index 77d54a605e5c..1e22c64c8f29 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
@@ -14,6 +14,7 @@
test_dependencies = ["Customer", "Supplier", "Accounting Dimension"]
+
class TestOpeningInvoiceCreationTool(FrappeTestCase):
@classmethod
def setUpClass(self):
@@ -22,10 +23,24 @@ def setUpClass(self):
create_dimension()
return super().setUpClass()
- def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None, invoice_number=None, department=None):
+ def make_invoices(
+ self,
+ invoice_type="Sales",
+ company=None,
+ party_1=None,
+ party_2=None,
+ invoice_number=None,
+ department=None,
+ ):
doc = frappe.get_single("Opening Invoice Creation Tool")
- args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
- party_1=party_1, party_2=party_2, invoice_number=invoice_number, department=department)
+ args = get_opening_invoice_creation_dict(
+ invoice_type=invoice_type,
+ company=company,
+ party_1=party_1,
+ party_2=party_2,
+ invoice_number=invoice_number,
+ department=department,
+ )
doc.update(args)
return doc.make_invoices()
@@ -68,15 +83,30 @@ def test_opening_sales_invoice_creation_with_missing_debit_account(self):
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"):
- cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company",
- "is_group": 1, "company": "_Test Opening Invoice Company"})
+ cc = frappe.get_doc(
+ {
+ "doctype": "Cost Center",
+ "cost_center_name": "_Test Opening Invoice Company",
+ "is_group": 1,
+ "company": "_Test Opening Invoice Company",
+ }
+ )
cc.insert(ignore_mandatory=True)
- cc2 = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "Main", "is_group": 0,
- "company": "_Test Opening Invoice Company", "parent_cost_center": cc.name})
+ cc2 = frappe.get_doc(
+ {
+ "doctype": "Cost Center",
+ "cost_center_name": "Main",
+ "is_group": 0,
+ "company": "_Test Opening Invoice Company",
+ "parent_cost_center": cc.name,
+ }
+ )
cc2.insert()
frappe.db.set_value("Company", company, "cost_center", "Main - _TOIC")
@@ -84,28 +114,37 @@ def test_opening_sales_invoice_creation_with_missing_debit_account(self):
self.make_invoices(company="_Test Opening Invoice Company", party_1=party_1, party_2=party_2)
# Check if missing debit account error raised
- error_log = frappe.db.exists("Error Log", {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]})
+ error_log = frappe.db.exists(
+ "Error Log",
+ {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]},
+ )
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"
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
- self.make_invoices(company=company, party_1=party_1, party_2=party_2, invoice_number="TEST-NEW-INV-11")
+ self.make_invoices(
+ company=company, party_1=party_1, party_2=party_2, invoice_number="TEST-NEW-INV-11"
+ )
- sales_inv1 = frappe.get_all('Sales Invoice', filters={'customer':'Customer A'})[0].get("name")
- sales_inv2 = frappe.get_all('Sales Invoice', filters={'customer':'Customer B'})[0].get("name")
+ sales_inv1 = frappe.get_all("Sales Invoice", filters={"customer": "Customer A"})[0].get("name")
+ sales_inv2 = frappe.get_all("Sales Invoice", filters={"customer": "Customer B"})[0].get("name")
self.assertEqual(sales_inv1, "TEST-NEW-INV-11")
- #teardown
+ # teardown
for inv in [sales_inv1, sales_inv2]:
- doc = frappe.get_doc('Sales Invoice', inv)
+ doc = frappe.get_doc("Sales Invoice", inv)
doc.cancel()
def test_opening_invoice_with_accounting_dimension(self):
- invoices = self.make_invoices(invoice_type="Sales", company="_Test Opening Invoice Company", department='Sales - _TOIC')
+ invoices = self.make_invoices(
+ invoice_type="Sales", company="_Test Opening Invoice Company", department="Sales - _TOIC"
+ )
expected_value = {
"keys": ["customer", "outstanding_amount", "status", "department"],
@@ -117,40 +156,44 @@ def test_opening_invoice_with_accounting_dimension(self):
def tearDown(self):
disable_dimension()
+
def get_opening_invoice_creation_dict(**args):
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
company = args.get("company", "_Test Company")
- invoice_dict = frappe._dict({
- "company": company,
- "invoice_type": args.get("invoice_type", "Sales"),
- "invoices": [
- {
- "qty": 1.0,
- "outstanding_amount": 300,
- "party": args.get("party_1") or "_Test {0}".format(party),
- "item_name": "Opening Item",
- "due_date": "2016-09-10",
- "posting_date": "2016-09-05",
- "temporary_opening_account": get_temporary_opening_account(company),
- "invoice_number": args.get("invoice_number")
- },
- {
- "qty": 2.0,
- "outstanding_amount": 250,
- "party": args.get("party_2") or "_Test {0} 1".format(party),
- "item_name": "Opening Item",
- "due_date": "2016-09-10",
- "posting_date": "2016-09-05",
- "temporary_opening_account": get_temporary_opening_account(company),
- "invoice_number": None
- }
- ]
- })
+ invoice_dict = frappe._dict(
+ {
+ "company": company,
+ "invoice_type": args.get("invoice_type", "Sales"),
+ "invoices": [
+ {
+ "qty": 1.0,
+ "outstanding_amount": 300,
+ "party": args.get("party_1") or "_Test {0}".format(party),
+ "item_name": "Opening Item",
+ "due_date": "2016-09-10",
+ "posting_date": "2016-09-05",
+ "temporary_opening_account": get_temporary_opening_account(company),
+ "invoice_number": args.get("invoice_number"),
+ },
+ {
+ "qty": 2.0,
+ "outstanding_amount": 250,
+ "party": args.get("party_2") or "_Test {0} 1".format(party),
+ "item_name": "Opening Item",
+ "due_date": "2016-09-10",
+ "posting_date": "2016-09-05",
+ "temporary_opening_account": get_temporary_opening_account(company),
+ "invoice_number": None,
+ },
+ ],
+ }
+ )
invoice_dict.update(args)
return invoice_dict
+
def make_company():
if frappe.db.exists("Company", "_Test Opening Invoice Company"):
return frappe.get_doc("Company", "_Test Opening Invoice Company")
@@ -163,15 +206,18 @@ def make_company():
company.insert()
return company
+
def make_customer(customer=None):
customer_name = customer or "Opening Customer"
- customer = frappe.get_doc({
- "doctype": "Customer",
- "customer_name": customer_name,
- "customer_group": "All Customer Groups",
- "customer_type": "Company",
- "territory": "All Territories"
- })
+ customer = frappe.get_doc(
+ {
+ "doctype": "Customer",
+ "customer_name": customer_name,
+ "customer_group": "All Customer Groups",
+ "customer_type": "Company",
+ "territory": "All Territories",
+ }
+ )
if not frappe.db.exists("Customer", customer_name):
customer.insert(ignore_permissions=True)
return customer.name
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
index 5c19091c3fbc..ed8ff7c0f7a5 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
@@ -110,13 +110,12 @@
"description": "Reference number of the invoice from the previous system",
"fieldname": "invoice_number",
"fieldtype": "Data",
- "in_list_view": 1,
"label": "Invoice Number"
}
],
"istable": 1,
"links": [],
- "modified": "2021-12-17 19:25:06.053187",
+ "modified": "2022-03-21 19:31:45.382656",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Opening Invoice Creation Tool Item",
@@ -125,5 +124,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/party_link/party_link.py b/erpnext/accounts/doctype/party_link/party_link.py
index 031a9fa4db04..312cfd2c0a11 100644
--- a/erpnext/accounts/doctype/party_link/party_link.py
+++ b/erpnext/accounts/doctype/party_link/party_link.py
@@ -8,45 +8,55 @@
class PartyLink(Document):
def validate(self):
- if self.primary_role not in ['Customer', 'Supplier']:
- frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
- title=_("Invalid Primary Role"))
-
- existing_party_link = frappe.get_all('Party Link', {
- 'primary_party': self.primary_party,
- 'secondary_party': self.secondary_party
- }, pluck="primary_role")
+ if self.primary_role not in ["Customer", "Supplier"]:
+ frappe.throw(
+ _(
+ "Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."
+ ),
+ title=_("Invalid Primary Role"),
+ )
+
+ existing_party_link = frappe.get_all(
+ "Party Link",
+ {"primary_party": self.primary_party, "secondary_party": self.secondary_party},
+ pluck="primary_role",
+ )
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)
- ))
-
- existing_party_link = frappe.get_all('Party Link', {
- 'primary_party': self.secondary_party
- }, pluck="primary_role")
+ frappe.throw(
+ _("{} {} is already linked with {} {}").format(
+ self.primary_role, bold(self.primary_party), self.secondary_role, bold(self.secondary_party)
+ )
+ )
+
+ existing_party_link = frappe.get_all(
+ "Party Link", {"primary_party": self.secondary_party}, pluck="primary_role"
+ )
if existing_party_link:
- frappe.throw(_('{} {} is already linked with another {}')
- .format(self.secondary_role, self.secondary_party, existing_party_link[0]))
-
- existing_party_link = frappe.get_all('Party Link', {
- 'secondary_party': self.primary_party
- }, pluck="primary_role")
+ frappe.throw(
+ _("{} {} is already linked with another {}").format(
+ self.secondary_role, self.secondary_party, existing_party_link[0]
+ )
+ )
+
+ existing_party_link = frappe.get_all(
+ "Party Link", {"secondary_party": self.primary_party}, pluck="primary_role"
+ )
if existing_party_link:
- frappe.throw(_('{} {} is already linked with another {}')
- .format(self.primary_role, self.primary_party, existing_party_link[0]))
+ frappe.throw(
+ _("{} {} is already linked with another {}").format(
+ self.primary_role, self.primary_party, existing_party_link[0]
+ )
+ )
@frappe.whitelist()
def create_party_link(primary_role, primary_party, secondary_party):
- party_link = frappe.new_doc('Party Link')
+ party_link = frappe.new_doc("Party Link")
party_link.primary_role = primary_role
party_link.primary_party = primary_party
- party_link.secondary_role = 'Customer' if primary_role == 'Supplier' else 'Supplier'
+ party_link.secondary_role = "Customer" if primary_role == "Supplier" else "Supplier"
party_link.secondary_party = secondary_party
party_link.save(ignore_permissions=True)
return party_link
-
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index b2b818a214dd..7315ae893651 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -532,7 +532,8 @@ frappe.ui.form.on('Payment Entry', {
to_currency: to_currency
},
callback: function(r, rt) {
- frm.set_value(exchange_rate_field, r.message);
+ const ex_rate = flt(r.message, frm.get_field(exchange_rate_field).get_precision());
+ frm.set_value(exchange_rate_field, ex_rate);
}
})
},
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index f9f33502d375..e6a554c00a23 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -95,7 +95,7 @@ def on_submit(self):
self.set_status()
def on_cancel(self):
- self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+ self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
self.make_gl_entries(cancel=1)
self.update_expense_claim()
self.update_outstanding_amounts()
@@ -107,6 +107,7 @@ def on_cancel(self):
def set_payment_req_status(self):
from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status
+
update_payment_req_status(self, None)
def update_outstanding_amounts(self):
@@ -116,8 +117,11 @@ def validate_duplicate_entry(self):
reference_names = []
for d in self.get("references"):
if (d.reference_doctype, d.reference_name, d.payment_term) in reference_names:
- frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
- .format(d.idx, d.reference_doctype, d.reference_name))
+ frappe.throw(
+ _("Row #{0}: Duplicate entry in References {1} {2}").format(
+ d.idx, d.reference_doctype, d.reference_name
+ )
+ )
reference_names.append((d.reference_doctype, d.reference_name, d.payment_term))
def set_bank_account_data(self):
@@ -133,20 +137,27 @@ def set_bank_account_data(self):
self.set(field, bank_data.account)
def validate_payment_type_with_outstanding(self):
- total_outstanding = sum(d.allocated_amount for d in self.get('references'))
- if total_outstanding < 0 and self.party_type == 'Customer' and self.payment_type == 'Receive':
- frappe.throw(_("Cannot receive from customer against negative outstanding"), title=_("Incorrect Payment Type"))
+ total_outstanding = sum(d.allocated_amount for d in self.get("references"))
+ if total_outstanding < 0 and self.party_type == "Customer" and self.payment_type == "Receive":
+ frappe.throw(
+ _("Cannot receive from customer against negative outstanding"),
+ title=_("Incorrect Payment Type"),
+ )
def validate_allocated_amount(self):
for d in self.get("references"):
- if (flt(d.allocated_amount))> 0:
+ if (flt(d.allocated_amount)) > 0:
if flt(d.allocated_amount) > flt(d.outstanding_amount):
- frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
+ frappe.throw(
+ _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)
+ )
# Check for negative outstanding invoices as well
if flt(d.allocated_amount) < 0:
if flt(d.allocated_amount) < flt(d.outstanding_amount):
- frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
+ frappe.throw(
+ _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)
+ )
def delink_advance_entry_references(self):
for reference in self.references:
@@ -156,9 +167,14 @@ def delink_advance_entry_references(self):
def set_missing_values(self):
if self.payment_type == "Internal Transfer":
- for field in ("party", "party_balance", "total_allocated_amount",
- "base_total_allocated_amount", "unallocated_amount"):
- self.set(field, None)
+ for field in (
+ "party",
+ "party_balance",
+ "total_allocated_amount",
+ "base_total_allocated_amount",
+ "unallocated_amount",
+ ):
+ self.set(field, None)
self.references = []
else:
if not self.party_type:
@@ -167,13 +183,16 @@ def set_missing_values(self):
if not self.party:
frappe.throw(_("Party is mandatory"))
- _party_name = "title" if self.party_type in ("Student", "Shareholder") else self.party_type.lower() + "_name"
+ _party_name = (
+ "title" if self.party_type in ("Student", "Shareholder") else self.party_type.lower() + "_name"
+ )
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
if self.party:
if not self.party_balance:
- self.party_balance = get_balance_on(party_type=self.party_type,
- party=self.party, date=self.posting_date, company=self.company)
+ self.party_balance = get_balance_on(
+ party_type=self.party_type, party=self.party, date=self.posting_date, company=self.company
+ )
if not self.party_account:
party_account = get_party_account(self.party_type, self.party, self.company)
@@ -190,16 +209,20 @@ def set_missing_values(self):
self.paid_to_account_currency = acc.account_currency
self.paid_to_account_balance = acc.account_balance
- self.party_account_currency = self.paid_from_account_currency \
- if self.payment_type=="Receive" else self.paid_to_account_currency
+ self.party_account_currency = (
+ self.paid_from_account_currency
+ if self.payment_type == "Receive"
+ else self.paid_to_account_currency
+ )
self.set_missing_ref_details()
def set_missing_ref_details(self, force=False):
for d in self.get("references"):
if d.allocated_amount:
- ref_details = get_reference_details(d.reference_doctype,
- d.reference_name, self.party_account_currency)
+ ref_details = get_reference_details(
+ d.reference_doctype, d.reference_name, self.party_account_currency
+ )
for field, value in ref_details.items():
if d.exchange_gain_loss:
@@ -209,7 +232,7 @@ def set_missing_ref_details(self, force=False):
# refer -> `update_reference_in_payment_entry()` in utils.py
continue
- if field == 'exchange_rate' or not d.get(field) or force:
+ if field == "exchange_rate" or not d.get(field) or force:
d.db_set(field, value)
def validate_payment_type(self):
@@ -222,8 +245,9 @@ def validate_party_details(self):
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
if self.party_account and self.party_type in ("Customer", "Supplier"):
- self.validate_account_type(self.party_account,
- [erpnext.get_party_account_type(self.party_type)])
+ self.validate_account_type(
+ self.party_account, [erpnext.get_party_account_type(self.party_type)]
+ )
def validate_bank_accounts(self):
if self.payment_type in ("Pay", "Internal Transfer"):
@@ -251,8 +275,9 @@ def set_source_exchange_rate(self, ref_doc=None):
self.source_exchange_rate = ref_doc.get("exchange_rate")
if not self.source_exchange_rate:
- self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency,
- self.company_currency, self.posting_date)
+ self.source_exchange_rate = get_exchange_rate(
+ self.paid_from_account_currency, self.company_currency, self.posting_date
+ )
def set_target_exchange_rate(self, ref_doc=None):
if self.paid_from_account_currency == self.paid_to_account_currency:
@@ -263,8 +288,9 @@ def set_target_exchange_rate(self, ref_doc=None):
self.target_exchange_rate = ref_doc.get("exchange_rate")
if not self.target_exchange_rate:
- self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency,
- self.company_currency, self.posting_date)
+ self.target_exchange_rate = get_exchange_rate(
+ self.paid_to_account_currency, self.company_currency, self.posting_date
+ )
def validate_mandatory(self):
for field in ("paid_amount", "received_amount", "source_exchange_rate", "target_exchange_rate"):
@@ -273,7 +299,7 @@ def validate_mandatory(self):
def validate_reference_documents(self):
if self.party_type == "Student":
- valid_reference_doctypes = ("Fees")
+ valid_reference_doctypes = "Fees"
elif self.party_type == "Customer":
valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
elif self.party_type == "Supplier":
@@ -281,14 +307,15 @@ def validate_reference_documents(self):
elif self.party_type == "Employee":
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
elif self.party_type == "Shareholder":
- valid_reference_doctypes = ("Journal Entry")
+ valid_reference_doctypes = "Journal Entry"
for d in self.get("references"):
if not d.allocated_amount:
continue
if d.reference_doctype not in valid_reference_doctypes:
- frappe.throw(_("Reference Doctype must be one of {0}")
- .format(comma_or(valid_reference_doctypes)))
+ frappe.throw(
+ _("Reference Doctype must be one of {0}").format(comma_or(valid_reference_doctypes))
+ )
elif d.reference_name:
if not frappe.db.exists(d.reference_doctype, d.reference_name):
@@ -298,28 +325,35 @@ def validate_reference_documents(self):
if d.reference_doctype != "Journal Entry":
if self.party != ref_doc.get(scrub(self.party_type)):
- frappe.throw(_("{0} {1} is not associated with {2} {3}")
- .format(d.reference_doctype, d.reference_name, self.party_type, self.party))
+ frappe.throw(
+ _("{0} {1} is not associated with {2} {3}").format(
+ d.reference_doctype, d.reference_name, self.party_type, self.party
+ )
+ )
else:
self.validate_journal_entry()
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
if self.party_type == "Customer":
- ref_party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
+ ref_party_account = (
+ get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
+ )
elif self.party_type == "Student":
ref_party_account = ref_doc.receivable_account
- elif self.party_type=="Supplier":
+ elif self.party_type == "Supplier":
ref_party_account = ref_doc.credit_to
- elif self.party_type=="Employee":
+ elif self.party_type == "Employee":
ref_party_account = ref_doc.payable_account
if ref_party_account != self.party_account:
- 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))
+ 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
+ )
+ )
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 validate_paid_invoices(self):
no_oustanding_refs = {}
@@ -329,28 +363,45 @@ def validate_paid_invoices(self):
continue
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
- outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
+ outstanding_amount, is_return = frappe.get_cached_value(
+ d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]
+ )
if outstanding_amount <= 0 and not is_return:
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
for k, v in no_oustanding_refs.items():
frappe.msgprint(
- _("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.")
- .format(_(k), frappe.bold(", ".join(d.reference_name for d in v)), frappe.bold(_("negative outstanding amount")))
- + "
" + _("If this is undesirable please cancel the corresponding Payment Entry."),
- title=_("Warning"), indicator="orange")
+ _(
+ "{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry."
+ ).format(
+ _(k),
+ frappe.bold(", ".join(d.reference_name for d in v)),
+ frappe.bold(_("negative outstanding amount")),
+ )
+ + "
"
+ + _("If this is undesirable please cancel the corresponding Payment Entry."),
+ title=_("Warning"),
+ indicator="orange",
+ )
def validate_journal_entry(self):
for d in self.get("references"):
if d.allocated_amount and d.reference_doctype == "Journal Entry":
- je_accounts = frappe.db.sql("""select debit, credit from `tabJournal Entry Account`
+ je_accounts = frappe.db.sql(
+ """select debit, credit from `tabJournal Entry Account`
where account = %s and party=%s and docstatus = 1 and parent = %s
and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
- """, (self.party_account, self.party, d.reference_name), as_dict=True)
+ """,
+ (self.party_account, self.party, d.reference_name),
+ as_dict=True,
+ )
if not je_accounts:
- frappe.throw(_("Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher")
- .format(d.idx, d.reference_name, self.party_account))
+ frappe.throw(
+ _(
+ "Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher"
+ ).format(d.idx, d.reference_name, self.party_account)
+ )
else:
dr_or_cr = "debit" if self.payment_type == "Receive" else "credit"
valid = False
@@ -358,14 +409,17 @@ def validate_journal_entry(self):
if flt(jvd[dr_or_cr]) > 0:
valid = True
if not valid:
- frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
- .format(d.reference_name, dr_or_cr))
+ frappe.throw(
+ _("Against Journal Entry {0} does not have any unmatched {1} entry").format(
+ d.reference_name, dr_or_cr
+ )
+ )
def update_payment_schedule(self, cancel=0):
invoice_payment_amount_map = {}
invoice_paid_amount_map = {}
- for ref in self.get('references'):
+ for ref in self.get("references"):
if ref.payment_term and ref.reference_name:
key = (ref.payment_term, ref.reference_name)
invoice_payment_amount_map.setdefault(key, 0.0)
@@ -373,58 +427,68 @@ def update_payment_schedule(self, cancel=0):
if not invoice_paid_amount_map.get(key):
payment_schedule = frappe.get_all(
- 'Payment Schedule',
- filters={'parent': ref.reference_name},
- fields=['paid_amount', 'payment_amount', 'payment_term', 'discount', 'outstanding']
+ "Payment Schedule",
+ filters={"parent": ref.reference_name},
+ fields=["paid_amount", "payment_amount", "payment_term", "discount", "outstanding"],
)
for term in payment_schedule:
invoice_key = (term.payment_term, ref.reference_name)
invoice_paid_amount_map.setdefault(invoice_key, {})
- invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding
- invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
+ invoice_paid_amount_map[invoice_key]["outstanding"] = term.outstanding
+ invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * (
+ term.discount / 100
+ )
for idx, (key, allocated_amount) in enumerate(invoice_payment_amount_map.items(), 1):
if not invoice_paid_amount_map.get(key):
- frappe.throw(_('Payment term {0} not used in {1}').format(key[0], key[1]))
+ frappe.throw(_("Payment term {0} not used in {1}").format(key[0], key[1]))
- outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
- discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get('discounted_amt'))
+ outstanding = flt(invoice_paid_amount_map.get(key, {}).get("outstanding"))
+ discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get("discounted_amt"))
if cancel:
- frappe.db.sql("""
+ frappe.db.sql(
+ """
UPDATE `tabPayment Schedule`
SET
paid_amount = `paid_amount` - %s,
discounted_amount = `discounted_amount` - %s,
outstanding = `outstanding` + %s
WHERE parent = %s and payment_term = %s""",
- (allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
+ (allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]),
+ )
else:
if allocated_amount > outstanding:
- frappe.throw(_('Row #{0}: Cannot allocate more than {1} against payment term {2}').format(idx, outstanding, key[0]))
+ frappe.throw(
+ _("Row #{0}: Cannot allocate more than {1} against payment term {2}").format(
+ idx, outstanding, key[0]
+ )
+ )
if allocated_amount and outstanding:
- frappe.db.sql("""
+ frappe.db.sql(
+ """
UPDATE `tabPayment Schedule`
SET
paid_amount = `paid_amount` + %s,
discounted_amount = `discounted_amount` + %s,
outstanding = `outstanding` - %s
WHERE parent = %s and payment_term = %s""",
- (allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
+ (allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]),
+ )
def set_status(self):
if self.docstatus == 2:
- self.status = 'Cancelled'
+ self.status = "Cancelled"
elif self.docstatus == 1:
- self.status = 'Submitted'
+ self.status = "Submitted"
else:
- self.status = 'Draft'
+ self.status = "Draft"
- self.db_set('status', self.status, update_modified = True)
+ self.db_set("status", self.status, update_modified=True)
def set_tax_withholding(self):
- if not self.party_type == 'Supplier':
+ if not self.party_type == "Supplier":
return
if not self.apply_tax_withholding_amount:
@@ -433,22 +497,24 @@ def set_tax_withholding(self):
net_total = self.paid_amount
# Adding args as purchase invoice to get TDS amount
- args = frappe._dict({
- 'company': self.company,
- 'doctype': 'Payment Entry',
- 'supplier': self.party,
- 'posting_date': self.posting_date,
- 'net_total': net_total
- })
+ args = frappe._dict(
+ {
+ "company": self.company,
+ "doctype": "Payment Entry",
+ "supplier": self.party,
+ "posting_date": self.posting_date,
+ "net_total": net_total,
+ }
+ )
tax_withholding_details = get_party_tax_withholding_details(args, self.tax_withholding_category)
if not tax_withholding_details:
return
- tax_withholding_details.update({
- 'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company)
- })
+ tax_withholding_details.update(
+ {"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company)}
+ )
accounts = []
for d in self.taxes:
@@ -456,7 +522,7 @@ def set_tax_withholding(self):
# 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})
+ tax_withholding_details.update({"included_in_paid_amount": d.included_in_paid_amount})
d.update(tax_withholding_details)
accounts.append(d.account_head)
@@ -464,8 +530,11 @@ def set_tax_withholding(self):
if not accounts or tax_withholding_details.get("account_head") not in accounts:
self.append("taxes", tax_withholding_details)
- to_remove = [d for d in self.taxes
- if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
+ to_remove = [
+ d
+ for d in self.taxes
+ if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")
+ ]
for d in to_remove:
self.remove(d)
@@ -492,40 +561,53 @@ def validate_received_amount(self):
def set_received_amount(self):
self.base_received_amount = self.base_paid_amount
- if self.paid_from_account_currency == self.paid_to_account_currency \
- and not self.payment_type == 'Internal Transfer':
+ if (
+ self.paid_from_account_currency == self.paid_to_account_currency
+ and not self.payment_type == "Internal Transfer"
+ ):
self.received_amount = self.paid_amount
def set_amounts_after_tax(self):
applicable_tax = 0
base_applicable_tax = 0
- for tax in self.get('taxes'):
+ for tax in self.get("taxes"):
if not tax.included_in_paid_amount:
- amount = -1 * tax.tax_amount if tax.add_deduct_tax == 'Deduct' else tax.tax_amount
- base_amount = -1 * tax.base_tax_amount if tax.add_deduct_tax == 'Deduct' else tax.base_tax_amount
+ amount = -1 * tax.tax_amount if tax.add_deduct_tax == "Deduct" else tax.tax_amount
+ base_amount = (
+ -1 * tax.base_tax_amount if tax.add_deduct_tax == "Deduct" else tax.base_tax_amount
+ )
applicable_tax += amount
base_applicable_tax += base_amount
- self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(applicable_tax),
- self.precision("paid_amount_after_tax"))
- self.base_paid_amount_after_tax = flt(flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate),
- self.precision("base_paid_amount_after_tax"))
+ self.paid_amount_after_tax = flt(
+ flt(self.paid_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")
+ )
+ self.base_paid_amount_after_tax = flt(
+ flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate),
+ self.precision("base_paid_amount_after_tax"),
+ )
- self.received_amount_after_tax = flt(flt(self.received_amount) + flt(applicable_tax),
- self.precision("paid_amount_after_tax"))
- self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.target_exchange_rate),
- self.precision("base_paid_amount_after_tax"))
+ self.received_amount_after_tax = flt(
+ flt(self.received_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")
+ )
+ self.base_received_amount_after_tax = flt(
+ flt(self.received_amount_after_tax) * flt(self.target_exchange_rate),
+ self.precision("base_paid_amount_after_tax"),
+ )
def set_amounts_in_company_currency(self):
self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
if self.paid_amount:
- self.base_paid_amount = flt(flt(self.paid_amount) * flt(self.source_exchange_rate),
- self.precision("base_paid_amount"))
+ self.base_paid_amount = flt(
+ flt(self.paid_amount) * flt(self.source_exchange_rate), self.precision("base_paid_amount")
+ )
if self.received_amount:
- self.base_received_amount = flt(flt(self.received_amount) * flt(self.target_exchange_rate),
- self.precision("base_received_amount"))
+ self.base_received_amount = flt(
+ flt(self.received_amount) * flt(self.target_exchange_rate),
+ self.precision("base_received_amount"),
+ )
def set_total_allocated_amount(self):
if self.payment_type == "Internal Transfer":
@@ -535,8 +617,9 @@ def set_total_allocated_amount(self):
for d in self.get("references"):
if d.allocated_amount:
total_allocated_amount += flt(d.allocated_amount)
- base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate),
- self.precision("base_paid_amount"))
+ base_total_allocated_amount += flt(
+ flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
+ )
self.total_allocated_amount = abs(total_allocated_amount)
self.base_total_allocated_amount = abs(base_total_allocated_amount)
@@ -546,22 +629,33 @@ def set_unallocated_amount(self):
if self.party:
total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
included_taxes = self.get_included_taxes()
- if self.payment_type == "Receive" \
- and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
- and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
- self.unallocated_amount = (self.base_received_amount + total_deductions -
- self.base_total_allocated_amount) / self.source_exchange_rate
+ if (
+ self.payment_type == "Receive"
+ and self.base_total_allocated_amount < self.base_received_amount + total_deductions
+ and self.total_allocated_amount
+ < self.paid_amount + (total_deductions / self.source_exchange_rate)
+ ):
+ self.unallocated_amount = (
+ self.base_received_amount + total_deductions - self.base_total_allocated_amount
+ ) / self.source_exchange_rate
self.unallocated_amount -= included_taxes
- elif self.payment_type == "Pay" \
- and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \
- and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate):
- self.unallocated_amount = (self.base_paid_amount - (total_deductions +
- self.base_total_allocated_amount)) / self.target_exchange_rate
+ elif (
+ self.payment_type == "Pay"
+ and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions)
+ and self.total_allocated_amount
+ < self.received_amount + (total_deductions / self.target_exchange_rate)
+ ):
+ self.unallocated_amount = (
+ self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)
+ ) / self.target_exchange_rate
self.unallocated_amount -= included_taxes
def set_difference_amount(self):
- base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
- if self.payment_type == "Receive" else flt(self.target_exchange_rate))
+ base_unallocated_amount = flt(self.unallocated_amount) * (
+ flt(self.source_exchange_rate)
+ if self.payment_type == "Receive"
+ else flt(self.target_exchange_rate)
+ )
base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
@@ -575,14 +669,15 @@ def set_difference_amount(self):
total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
included_taxes = self.get_included_taxes()
- self.difference_amount = flt(self.difference_amount - total_deductions - included_taxes,
- self.precision("difference_amount"))
+ self.difference_amount = flt(
+ self.difference_amount - total_deductions - included_taxes, self.precision("difference_amount")
+ )
def get_included_taxes(self):
included_taxes = 0
- for tax in self.get('taxes'):
+ for tax in self.get("taxes"):
if tax.included_in_paid_amount:
- if tax.add_deduct_tax == 'Add':
+ if tax.add_deduct_tax == "Add":
included_taxes += tax.base_tax_amount
else:
included_taxes -= tax.base_tax_amount
@@ -593,27 +688,41 @@ def get_included_taxes(self):
# Clear the reference document which doesn't have allocated amount on validate so that form can be loaded fast
def clear_unallocated_reference_document_rows(self):
self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]}))
- frappe.db.sql("""delete from `tabPayment Entry Reference`
- where parent = %s and allocated_amount = 0""", self.name)
+ frappe.db.sql(
+ """delete from `tabPayment Entry Reference`
+ where parent = %s and allocated_amount = 0""",
+ self.name,
+ )
def validate_payment_against_negative_invoice(self):
- if ((self.payment_type=="Pay" and self.party_type=="Customer")
- or (self.payment_type=="Receive" and self.party_type=="Supplier")):
+ if (self.payment_type == "Pay" and self.party_type == "Customer") or (
+ self.payment_type == "Receive" and self.party_type == "Supplier"
+ ):
- total_negative_outstanding = sum(abs(flt(d.outstanding_amount))
- for d in self.get("references") if flt(d.outstanding_amount) < 0)
+ total_negative_outstanding = sum(
+ abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
+ )
- paid_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
+ paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
additional_charges = sum([flt(d.amount) for d in self.deductions])
if not total_negative_outstanding:
- frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
- .format(_(self.payment_type), (_("to") if self.party_type=="Customer" else _("from")),
- self.party_type), InvalidPaymentEntry)
+ frappe.throw(
+ _("Cannot {0} {1} {2} without any negative outstanding invoice").format(
+ _(self.payment_type),
+ (_("to") if self.party_type == "Customer" else _("from")),
+ self.party_type,
+ ),
+ InvalidPaymentEntry,
+ )
elif paid_amount - additional_charges > total_negative_outstanding:
- frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}")
- .format(total_negative_outstanding), InvalidPaymentEntry)
+ frappe.throw(
+ _("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
+ total_negative_outstanding
+ ),
+ InvalidPaymentEntry,
+ )
def set_title(self):
if frappe.flags.in_import and self.title:
@@ -634,33 +743,45 @@ def validate_transaction_reference(self):
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
def set_remarks(self):
- if self.custom_remarks: return
+ if self.custom_remarks:
+ return
- if self.payment_type=="Internal Transfer":
- remarks = [_("Amount {0} {1} transferred from {2} to {3}")
- .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
+ if self.payment_type == "Internal Transfer":
+ remarks = [
+ _("Amount {0} {1} transferred from {2} to {3}").format(
+ self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to
+ )
+ ]
else:
- remarks = [_("Amount {0} {1} {2} {3}").format(
- self.party_account_currency,
- self.paid_amount if self.payment_type=="Receive" else self.received_amount,
- _("received from") if self.payment_type=="Receive" else _("to"), self.party
- )]
+ remarks = [
+ _("Amount {0} {1} {2} {3}").format(
+ self.party_account_currency,
+ self.paid_amount if self.payment_type == "Receive" else self.received_amount,
+ _("received from") if self.payment_type == "Receive" else _("to"),
+ self.party,
+ )
+ ]
if self.reference_no:
- remarks.append(_("Transaction reference no {0} dated {1}")
- .format(self.reference_no, self.reference_date))
+ remarks.append(
+ _("Transaction reference no {0} dated {1}").format(self.reference_no, self.reference_date)
+ )
if self.payment_type in ["Receive", "Pay"]:
for d in self.get("references"):
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))
+ remarks.append(
+ _("Amount {0} {1} against {2} {3}").format(
+ 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))
+ remarks.append(
+ _("Amount {0} {1} deducted against {2}").format(self.company_currency, d.amount, d.account)
+ )
self.set("remarks", "\n".join(remarks))
@@ -679,92 +800,110 @@ def make_gl_entries(self, cancel=0, adv_adj=0):
def add_party_gl_entries(self, gl_entries):
if self.party_account:
- if self.payment_type=="Receive":
+ if self.payment_type == "Receive":
against_account = self.paid_to
else:
against_account = self.paid_from
- party_gl_dict = self.get_gl_dict({
- "account": self.party_account,
- "party_type": self.party_type,
- "party": self.party,
- "against": against_account,
- "account_currency": self.party_account_currency,
- "cost_center": self.cost_center
- }, item=self)
+ party_gl_dict = self.get_gl_dict(
+ {
+ "account": self.party_account,
+ "party_type": self.party_type,
+ "party": self.party,
+ "against": against_account,
+ "account_currency": self.party_account_currency,
+ "cost_center": self.cost_center,
+ },
+ item=self,
+ )
- dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
+ dr_or_cr = (
+ "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit"
+ )
for d in self.get("references"):
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")
gle = party_gl_dict.copy()
- gle.update({
- "against_voucher_type": d.reference_doctype,
- "against_voucher": d.reference_name,
- "cost_center": cost_center
- })
+ gle.update(
+ {
+ "against_voucher_type": d.reference_doctype,
+ "against_voucher": d.reference_name,
+ "cost_center": cost_center,
+ }
+ )
- allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
- self.precision("paid_amount"))
+ allocated_amount_in_company_currency = flt(
+ flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("paid_amount")
+ )
- gle.update({
- dr_or_cr + "_in_account_currency": d.allocated_amount,
- dr_or_cr: allocated_amount_in_company_currency
- })
+ gle.update(
+ {
+ dr_or_cr + "_in_account_currency": d.allocated_amount,
+ dr_or_cr: allocated_amount_in_company_currency,
+ }
+ )
gl_entries.append(gle)
if self.unallocated_amount:
exchange_rate = self.get_exchange_rate()
- base_unallocated_amount = (self.unallocated_amount * exchange_rate)
+ base_unallocated_amount = self.unallocated_amount * exchange_rate
gle = party_gl_dict.copy()
- gle.update({
- dr_or_cr + "_in_account_currency": self.unallocated_amount,
- dr_or_cr: base_unallocated_amount
- })
+ gle.update(
+ {
+ dr_or_cr + "_in_account_currency": self.unallocated_amount,
+ dr_or_cr: base_unallocated_amount,
+ }
+ )
gl_entries.append(gle)
def add_bank_gl_entries(self, gl_entries):
if self.payment_type in ("Pay", "Internal Transfer"):
gl_entries.append(
- self.get_gl_dict({
- "account": self.paid_from,
- "account_currency": self.paid_from_account_currency,
- "against": self.party if self.payment_type=="Pay" else self.paid_to,
- "credit_in_account_currency": self.paid_amount,
- "credit": self.base_paid_amount,
- "cost_center": self.cost_center,
- "post_net_value": True
- }, item=self)
+ self.get_gl_dict(
+ {
+ "account": self.paid_from,
+ "account_currency": self.paid_from_account_currency,
+ "against": self.party if self.payment_type == "Pay" else self.paid_to,
+ "credit_in_account_currency": self.paid_amount,
+ "credit": self.base_paid_amount,
+ "cost_center": self.cost_center,
+ "post_net_value": True,
+ },
+ item=self,
+ )
)
if self.payment_type in ("Receive", "Internal Transfer"):
gl_entries.append(
- self.get_gl_dict({
- "account": self.paid_to,
- "account_currency": self.paid_to_account_currency,
- "against": self.party if self.payment_type=="Receive" else self.paid_from,
- "debit_in_account_currency": self.received_amount,
- "debit": self.base_received_amount,
- "cost_center": self.cost_center
- }, item=self)
+ self.get_gl_dict(
+ {
+ "account": self.paid_to,
+ "account_currency": self.paid_to_account_currency,
+ "against": self.party if self.payment_type == "Receive" else self.paid_from,
+ "debit_in_account_currency": self.received_amount,
+ "debit": self.base_received_amount,
+ "cost_center": self.cost_center,
+ },
+ item=self,
+ )
)
def add_tax_gl_entries(self, gl_entries):
- for d in self.get('taxes'):
+ for d in self.get("taxes"):
account_currency = get_account_currency(d.account_head)
if account_currency != self.company_currency:
frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency))
- if self.payment_type in ('Pay', 'Internal Transfer'):
+ if self.payment_type in ("Pay", "Internal Transfer"):
dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
against = self.party or self.paid_from
- elif self.payment_type == 'Receive':
+ elif self.payment_type == "Receive":
dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
against = self.party or self.paid_to
@@ -774,29 +913,39 @@ def add_tax_gl_entries(self, gl_entries):
base_tax_amount = d.base_tax_amount
gl_entries.append(
- self.get_gl_dict({
- "account": d.account_head,
- "against": against,
- dr_or_cr: tax_amount,
- dr_or_cr + "_in_account_currency": base_tax_amount
- if account_currency==self.company_currency
- else d.tax_amount,
- "cost_center": d.cost_center,
- "post_net_value": True,
- }, account_currency, item=d))
-
- if not d.included_in_paid_amount:
- gl_entries.append(
- self.get_gl_dict({
- "account": payment_account,
+ self.get_gl_dict(
+ {
+ "account": d.account_head,
"against": against,
- rev_dr_or_cr: tax_amount,
- rev_dr_or_cr + "_in_account_currency": base_tax_amount
- if account_currency==self.company_currency
+ dr_or_cr: tax_amount,
+ dr_or_cr + "_in_account_currency": base_tax_amount
+ if account_currency == self.company_currency
else d.tax_amount,
- "cost_center": self.cost_center,
+ "cost_center": d.cost_center,
"post_net_value": True,
- }, account_currency, item=d))
+ },
+ account_currency,
+ item=d,
+ )
+ )
+
+ if not d.included_in_paid_amount:
+ gl_entries.append(
+ self.get_gl_dict(
+ {
+ "account": payment_account,
+ "against": against,
+ rev_dr_or_cr: tax_amount,
+ rev_dr_or_cr + "_in_account_currency": base_tax_amount
+ if account_currency == self.company_currency
+ else d.tax_amount,
+ "cost_center": self.cost_center,
+ "post_net_value": True,
+ },
+ account_currency,
+ item=d,
+ )
+ )
def add_deductions_gl_entries(self, gl_entries):
for d in self.get("deductions"):
@@ -806,33 +955,40 @@ def add_deductions_gl_entries(self, gl_entries):
frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
gl_entries.append(
- self.get_gl_dict({
- "account": d.account,
- "account_currency": account_currency,
- "against": self.party or self.paid_from,
- "debit_in_account_currency": d.amount,
- "debit": d.amount,
- "cost_center": d.cost_center
- }, item=d)
+ self.get_gl_dict(
+ {
+ "account": d.account,
+ "account_currency": account_currency,
+ "against": self.party or self.paid_from,
+ "debit_in_account_currency": d.amount,
+ "debit": d.amount,
+ "cost_center": d.cost_center,
+ },
+ item=d,
+ )
)
def get_party_account_for_taxes(self):
- if self.payment_type == 'Receive':
+ if self.payment_type == "Receive":
return self.paid_to
- elif self.payment_type in ('Pay', 'Internal Transfer'):
+ elif self.payment_type in ("Pay", "Internal Transfer"):
return self.paid_from
def update_advance_paid(self):
if self.payment_type in ("Receive", "Pay") and self.party:
for d in self.get("references"):
- if d.allocated_amount \
- and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance", "Gratuity"):
- frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
+ if d.allocated_amount and d.reference_doctype in (
+ "Sales Order",
+ "Purchase Order",
+ "Employee Advance",
+ "Gratuity",
+ ):
+ frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
def update_expense_claim(self):
if self.payment_type in ("Pay") and self.party:
for d in self.get("references"):
- if d.reference_doctype=="Expense Claim" and d.reference_name:
+ if d.reference_doctype == "Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
if self.docstatus == 2:
update_reimbursed_amount(doc, -1 * d.allocated_amount)
@@ -845,31 +1001,29 @@ def on_recurring(self, reference_doc, auto_repeat_doc):
def calculate_deductions(self, tax_details):
return {
- "account": tax_details['tax']['account_head'],
- "cost_center": frappe.get_cached_value('Company', self.company, "cost_center"),
- "amount": self.total_allocated_amount * (tax_details['tax']['rate'] / 100)
+ "account": tax_details["tax"]["account_head"],
+ "cost_center": frappe.get_cached_value("Company", self.company, "cost_center"),
+ "amount": self.total_allocated_amount * (tax_details["tax"]["rate"] / 100),
}
def set_gain_or_loss(self, account_details=None):
if not self.difference_amount:
self.set_difference_amount()
- row = {
- 'amount': self.difference_amount
- }
+ row = {"amount": self.difference_amount}
if account_details:
row.update(account_details)
- if not row.get('amount'):
+ if not row.get("amount"):
# if no difference amount
return
- self.append('deductions', row)
+ self.append("deductions", row)
self.set_unallocated_amount()
def get_exchange_rate(self):
- return self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate
+ return self.source_exchange_rate if self.payment_type == "Receive" else self.target_exchange_rate
def initialize_taxes(self):
for tax in self.get("taxes"):
@@ -893,25 +1047,31 @@ def determine_exclusive_rate(self):
cumulated_tax_fraction = 0
for i, tax in enumerate(self.get("taxes")):
tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax)
- if i==0:
+ if i == 0:
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
else:
- tax.grand_total_fraction_for_current_item = \
- self.get("taxes")[i-1].grand_total_fraction_for_current_item \
+ tax.grand_total_fraction_for_current_item = (
+ self.get("taxes")[i - 1].grand_total_fraction_for_current_item
+ tax.tax_fraction_for_current_item
+ )
cumulated_tax_fraction += tax.tax_fraction_for_current_item
- self.paid_amount_after_tax = flt(self.paid_amount/(1+cumulated_tax_fraction))
+ self.paid_amount_after_tax = flt(self.paid_amount / (1 + cumulated_tax_fraction))
def calculate_taxes(self):
self.total_taxes_and_charges = 0.0
self.base_total_taxes_and_charges = 0.0
- actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
- for tax in self.get("taxes") if tax.charge_type == "Actual"])
+ actual_tax_dict = dict(
+ [
+ [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
+ for tax in self.get("taxes")
+ if tax.charge_type == "Actual"
+ ]
+ )
- for i, tax in enumerate(self.get('taxes')):
+ for i, tax in enumerate(self.get("taxes")):
current_tax_amount = self.get_current_tax_amount(tax)
if tax.charge_type == "Actual":
@@ -930,19 +1090,21 @@ def calculate_taxes(self):
if i == 0:
tax.total = flt(self.paid_amount_after_tax + current_tax_amount, self.precision("total", tax))
else:
- tax.total = flt(self.get('taxes')[i-1].total + current_tax_amount, self.precision("total", tax))
+ tax.total = flt(
+ self.get("taxes")[i - 1].total + current_tax_amount, self.precision("total", tax)
+ )
tax.base_total = tax.total * self.source_exchange_rate
- if self.payment_type == 'Pay':
+ if self.payment_type == "Pay":
self.base_total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
else:
self.base_total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
- if self.get('taxes'):
- self.paid_amount_after_tax = self.get('taxes')[-1].base_total
+ if self.get("taxes"):
+ self.paid_amount_after_tax = self.get("taxes")[-1].base_total
def get_current_tax_amount(self, tax):
tax_rate = tax.rate
@@ -950,7 +1112,11 @@ def get_current_tax_amount(self, tax):
# To set row_id by default as previous row.
if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]:
if tax.idx == 1:
- frappe.throw(_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"))
+ frappe.throw(
+ _(
+ "Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"
+ )
+ )
if not tax.row_id:
tax.row_id = tax.idx - 1
@@ -960,12 +1126,10 @@ def get_current_tax_amount(self, tax):
elif tax.charge_type == "On Paid Amount":
current_tax_amount = (tax_rate / 100.0) * self.paid_amount_after_tax
elif tax.charge_type == "On Previous Row Amount":
- current_tax_amount = (tax_rate / 100.0) * \
- self.get('taxes')[cint(tax.row_id) - 1].tax_amount
+ current_tax_amount = (tax_rate / 100.0) * self.get("taxes")[cint(tax.row_id) - 1].tax_amount
elif tax.charge_type == "On Previous Row Total":
- current_tax_amount = (tax_rate / 100.0) * \
- self.get('taxes')[cint(tax.row_id) - 1].total
+ current_tax_amount = (tax_rate / 100.0) * self.get("taxes")[cint(tax.row_id) - 1].total
return current_tax_amount
@@ -978,83 +1142,106 @@ def get_current_tax_fraction(self, tax):
if tax.charge_type == "On Paid Amount":
current_tax_fraction = tax_rate / 100.0
elif tax.charge_type == "On Previous Row Amount":
- current_tax_fraction = (tax_rate / 100.0) * \
- self.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
+ current_tax_fraction = (tax_rate / 100.0) * self.get("taxes")[
+ cint(tax.row_id) - 1
+ ].tax_fraction_for_current_item
elif tax.charge_type == "On Previous Row Total":
- current_tax_fraction = (tax_rate / 100.0) * \
- self.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
+ current_tax_fraction = (tax_rate / 100.0) * self.get("taxes")[
+ cint(tax.row_id) - 1
+ ].grand_total_fraction_for_current_item
if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
current_tax_fraction *= -1.0
return current_tax_fraction
+
def validate_inclusive_tax(tax, doc):
def _on_previous_row_error(row_range):
- throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
+ throw(
+ _("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(
+ tax.idx, row_range
+ )
+ )
if cint(getattr(tax, "included_in_paid_amount", None)):
if tax.charge_type == "Actual":
# inclusive tax cannot be of type Actual
- throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
- elif tax.charge_type == "On Previous Row Amount" and \
- not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount):
+ throw(
+ _("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(
+ tax.idx
+ )
+ )
+ elif tax.charge_type == "On Previous Row Amount" and not cint(
+ doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount
+ ):
# referred row should also be inclusive
_on_previous_row_error(tax.row_id)
- elif tax.charge_type == "On Previous Row Total" and \
- not all([cint(t.included_in_paid_amount for t in doc.get("taxes")[:cint(tax.row_id) - 1])]):
+ elif tax.charge_type == "On Previous Row Total" and not all(
+ [cint(t.included_in_paid_amount for t in doc.get("taxes")[: cint(tax.row_id) - 1])]
+ ):
# all rows about the referred tax should be inclusive
_on_previous_row_error("1 - %d" % (cint(tax.row_id),))
elif tax.get("category") == "Valuation":
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
+
@frappe.whitelist()
def get_outstanding_reference_documents(args):
if isinstance(args, str):
args = json.loads(args)
- if args.get('party_type') == 'Member':
+ if args.get("party_type") == "Member":
return
# confirm that Supplier is not blocked
- if args.get('party_type') == 'Supplier':
- supplier_status = get_supplier_block_status(args['party'])
- if supplier_status['on_hold']:
- if supplier_status['hold_type'] == 'All':
+ if args.get("party_type") == "Supplier":
+ supplier_status = get_supplier_block_status(args["party"])
+ if supplier_status["on_hold"]:
+ if supplier_status["hold_type"] == "All":
return []
- elif supplier_status['hold_type'] == 'Payments':
- if not supplier_status['release_date'] or getdate(nowdate()) <= supplier_status['release_date']:
+ elif supplier_status["hold_type"] == "Payments":
+ if (
+ not supplier_status["release_date"] or getdate(nowdate()) <= supplier_status["release_date"]
+ ):
return []
party_account_currency = get_account_currency(args.get("party_account"))
- company_currency = frappe.get_cached_value('Company', args.get("company"), "default_currency")
+ company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency")
# Get positive outstanding sales /purchase invoices/ Fees
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
- condition = " and voucher_type={0} and voucher_no={1}"\
- .format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]))
+ condition = " and voucher_type={0} and voucher_no={1}".format(
+ frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])
+ )
# Add cost center condition
if args.get("cost_center"):
condition += " and cost_center='%s'" % args.get("cost_center")
date_fields_dict = {
- 'posting_date': ['from_posting_date', 'to_posting_date'],
- 'due_date': ['from_due_date', 'to_due_date']
+ "posting_date": ["from_posting_date", "to_posting_date"],
+ "due_date": ["from_due_date", "to_due_date"],
}
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(fieldname,
- args.get(date_fields[0]), args.get(date_fields[1]))
+ condition += " and {0} between '{1}' and '{2}'".format(
+ fieldname, args.get(date_fields[0]), args.get(date_fields[1])
+ )
if args.get("company"):
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
- outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
- args.get("party_account"), filters=args, condition=condition)
+ outstanding_invoices = get_outstanding_invoices(
+ args.get("party_type"),
+ args.get("party"),
+ args.get("party_account"),
+ filters=args,
+ condition=condition,
+ )
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
@@ -1065,28 +1252,44 @@ def get_outstanding_reference_documents(args):
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
elif d.voucher_type == "Journal Entry":
d["exchange_rate"] = get_exchange_rate(
- party_account_currency, company_currency, d.posting_date
+ party_account_currency, company_currency, d.posting_date
)
if d.voucher_type in ("Purchase Invoice"):
d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
# Get all SO / PO which are not fully billed or against which full advance not paid
orders_to_be_billed = []
- if (args.get("party_type") != "Student"):
- orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
- args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
+ if args.get("party_type") != "Student":
+ orders_to_be_billed = get_orders_to_be_billed(
+ args.get("posting_date"),
+ args.get("party_type"),
+ args.get("party"),
+ args.get("company"),
+ party_account_currency,
+ company_currency,
+ filters=args,
+ )
# Get negative outstanding sales /purchase invoices
negative_outstanding_invoices = []
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
- negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
- args.get("party_account"), party_account_currency, company_currency, condition=condition)
+ negative_outstanding_invoices = get_negative_outstanding_invoices(
+ args.get("party_type"),
+ args.get("party"),
+ args.get("party_account"),
+ party_account_currency,
+ company_currency,
+ condition=condition,
+ )
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
if not data:
- frappe.msgprint(_("No outstanding invoices found for the {0} {1} which qualify the filters you have specified.")
- .format(_(args.get("party_type")).lower(), frappe.bold(args.get("party"))))
+ frappe.msgprint(
+ _(
+ "No outstanding invoices found for the {0} {1} which qualify the filters you have specified."
+ ).format(_(args.get("party_type")).lower(), frappe.bold(args.get("party")))
+ )
return data
@@ -1094,53 +1297,75 @@ def get_outstanding_reference_documents(args):
def split_invoices_based_on_payment_terms(outstanding_invoices):
invoice_ref_based_on_payment_terms = {}
for idx, d in enumerate(outstanding_invoices):
- if d.voucher_type in ['Sales Invoice', 'Purchase Invoice']:
- payment_term_template = frappe.db.get_value(d.voucher_type, d.voucher_no, 'payment_terms_template')
+ if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
+ payment_term_template = frappe.db.get_value(
+ d.voucher_type, d.voucher_no, "payment_terms_template"
+ )
if payment_term_template:
allocate_payment_based_on_payment_terms = frappe.db.get_value(
- 'Payment Terms Template', payment_term_template, 'allocate_payment_based_on_payment_terms')
+ "Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
+ )
if allocate_payment_based_on_payment_terms:
- payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': d.voucher_no}, fields=["*"])
+ payment_schedule = frappe.get_all(
+ "Payment Schedule", filters={"parent": d.voucher_no}, fields=["*"]
+ )
for payment_term in payment_schedule:
if payment_term.outstanding > 0.1:
invoice_ref_based_on_payment_terms.setdefault(idx, [])
- invoice_ref_based_on_payment_terms[idx].append(frappe._dict({
- 'due_date': d.due_date,
- 'currency': d.currency,
- 'voucher_no': d.voucher_no,
- 'voucher_type': d.voucher_type,
- 'posting_date': d.posting_date,
- 'invoice_amount': flt(d.invoice_amount),
- 'outstanding_amount': flt(d.outstanding_amount),
- 'payment_amount': payment_term.payment_amount,
- 'payment_term': payment_term.payment_term
- }))
+ invoice_ref_based_on_payment_terms[idx].append(
+ frappe._dict(
+ {
+ "due_date": d.due_date,
+ "currency": d.currency,
+ "voucher_no": d.voucher_no,
+ "voucher_type": d.voucher_type,
+ "posting_date": d.posting_date,
+ "invoice_amount": flt(d.invoice_amount),
+ "outstanding_amount": flt(d.outstanding_amount),
+ "payment_amount": payment_term.payment_amount,
+ "payment_term": payment_term.payment_term,
+ }
+ )
+ )
outstanding_invoices_after_split = []
if invoice_ref_based_on_payment_terms:
for idx, ref in invoice_ref_based_on_payment_terms.items():
- voucher_no = ref[0]['voucher_no']
- voucher_type = ref[0]['voucher_type']
+ voucher_no = ref[0]["voucher_no"]
+ voucher_type = ref[0]["voucher_type"]
- frappe.msgprint(_("Spliting {} {} into {} row(s) as per Payment Terms").format(
- voucher_type, voucher_no, len(ref)), alert=True)
+ frappe.msgprint(
+ _("Spliting {} {} into {} row(s) as per Payment Terms").format(
+ voucher_type, voucher_no, len(ref)
+ ),
+ alert=True,
+ )
outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
- existing_row = list(filter(lambda x: x.get('voucher_no') == voucher_no, outstanding_invoices))
+ existing_row = list(filter(lambda x: x.get("voucher_no") == voucher_no, outstanding_invoices))
index = outstanding_invoices.index(existing_row[0])
outstanding_invoices.pop(index)
outstanding_invoices_after_split += outstanding_invoices
return outstanding_invoices_after_split
-def get_orders_to_be_billed(posting_date, party_type, party,
- company, party_account_currency, company_currency, cost_center=None, filters=None):
+
+def get_orders_to_be_billed(
+ posting_date,
+ party_type,
+ party,
+ company,
+ party_account_currency,
+ company_currency,
+ cost_center=None,
+ filters=None,
+):
if party_type == "Customer":
- voucher_type = 'Sales Order'
+ voucher_type = "Sales Order"
elif party_type == "Supplier":
- voucher_type = 'Purchase Order'
+ voucher_type = "Purchase Order"
elif party_type == "Employee":
voucher_type = None
@@ -1148,7 +1373,7 @@ def get_orders_to_be_billed(posting_date, party_type, party,
if voucher_type:
doc = frappe.get_doc({"doctype": voucher_type})
condition = ""
- if doc and hasattr(doc, 'cost_center'):
+ if doc and hasattr(doc, "cost_center"):
condition = " and cost_center='%s'" % cost_center
orders = []
@@ -1160,7 +1385,8 @@ def get_orders_to_be_billed(posting_date, party_type, party,
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
- orders = frappe.db.sql("""
+ orders = frappe.db.sql(
+ """
select
name as voucher_no,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
@@ -1178,18 +1404,25 @@ def get_orders_to_be_billed(posting_date, party_type, party,
{condition}
order by
transaction_date, name
- """.format(**{
- "rounded_total_field": rounded_total_field,
- "grand_total_field": grand_total_field,
- "voucher_type": voucher_type,
- "party_type": scrub(party_type),
- "condition": condition
- }), (party, company), as_dict=True)
+ """.format(
+ **{
+ "rounded_total_field": rounded_total_field,
+ "grand_total_field": grand_total_field,
+ "voucher_type": voucher_type,
+ "party_type": scrub(party_type),
+ "condition": condition,
+ }
+ ),
+ (party, company),
+ as_dict=True,
+ )
order_list = []
for d in orders:
- if not (flt(d.outstanding_amount) >= flt(filters.get("outstanding_amt_greater_than"))
- and flt(d.outstanding_amount) <= flt(filters.get("outstanding_amt_less_than"))):
+ if not (
+ flt(d.outstanding_amount) >= flt(filters.get("outstanding_amt_greater_than"))
+ and flt(d.outstanding_amount) <= flt(filters.get("outstanding_amt_less_than"))
+ ):
continue
d["voucher_type"] = voucher_type
@@ -1199,8 +1432,16 @@ def get_orders_to_be_billed(posting_date, party_type, party,
return order_list
-def get_negative_outstanding_invoices(party_type, party, party_account,
- party_account_currency, company_currency, cost_center=None, condition=None):
+
+def get_negative_outstanding_invoices(
+ party_type,
+ party,
+ party_account,
+ party_account_currency,
+ company_currency,
+ cost_center=None,
+ condition=None,
+):
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
@@ -1212,7 +1453,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account,
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
- return frappe.db.sql("""
+ return frappe.db.sql(
+ """
select
"{voucher_type}" as voucher_type, name as voucher_no,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
@@ -1227,21 +1469,26 @@ def get_negative_outstanding_invoices(party_type, party, party_account,
{condition}
order by
posting_date, name
- """.format(**{
- "supplier_condition": supplier_condition,
- "condition": condition,
- "rounded_total_field": rounded_total_field,
- "grand_total_field": grand_total_field,
- "voucher_type": voucher_type,
- "party_type": scrub(party_type),
- "party_account": "debit_to" if party_type == "Customer" else "credit_to",
- "cost_center": cost_center
- }), (party, party_account), as_dict=True)
+ """.format(
+ **{
+ "supplier_condition": supplier_condition,
+ "condition": condition,
+ "rounded_total_field": rounded_total_field,
+ "grand_total_field": grand_total_field,
+ "voucher_type": voucher_type,
+ "party_type": scrub(party_type),
+ "party_account": "debit_to" if party_type == "Customer" else "credit_to",
+ "cost_center": cost_center,
+ }
+ ),
+ (party, party_account),
+ as_dict=True,
+ )
@frappe.whitelist()
def get_party_details(company, party_type, party, date, cost_center=None):
- bank_account = ''
+ bank_account = ""
if not frappe.db.exists(party_type, party):
frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
@@ -1249,7 +1496,9 @@ def get_party_details(company, party_type, party, date, cost_center=None):
account_currency = get_account_currency(party_account)
account_balance = get_balance_on(party_account, date, cost_center=cost_center)
- _party_name = "title" if party_type in ("Student", "Shareholder") else party_type.lower() + "_name"
+ _party_name = (
+ "title" if party_type in ("Student", "Shareholder") else party_type.lower() + "_name"
+ )
party_name = frappe.db.get_value(party_type, party, _party_name)
party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center)
if party_type in ["Customer", "Supplier"]:
@@ -1261,61 +1510,68 @@ def get_party_details(company, party_type, party, date, cost_center=None):
"party_account_currency": account_currency,
"party_balance": party_balance,
"account_balance": account_balance,
- "bank_account": bank_account
+ "bank_account": bank_account,
}
@frappe.whitelist()
def get_account_details(account, date, cost_center=None):
- frappe.has_permission('Payment Entry', throw=True)
+ 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))
+ 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({
- "account_currency": get_account_currency(account),
- "account_balance": account_balance,
- "account_type": frappe.db.get_value("Account", account, "account_type")
- })
+ return frappe._dict(
+ {
+ "account_currency": get_account_currency(account),
+ "account_balance": account_balance,
+ "account_type": frappe.db.get_value("Account", account, "account_type"),
+ }
+ )
@frappe.whitelist()
def get_company_defaults(company):
fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
- ret = frappe.get_cached_value('Company', company, fields, as_dict=1)
+ ret = frappe.get_cached_value("Company", company, fields, as_dict=1)
for fieldname in fields:
if not ret[fieldname]:
- frappe.throw(_("Please set default {0} in Company {1}")
- .format(frappe.get_meta("Company").get_label(fieldname), company))
+ frappe.throw(
+ _("Please set default {0} in Company {1}").format(
+ frappe.get_meta("Company").get_label(fieldname), company
+ )
+ )
return ret
def get_outstanding_on_journal_entry(name):
res = frappe.db.sql(
- 'SELECT '
- 'CASE WHEN party_type IN ("Customer", "Student") '
- 'THEN ifnull(sum(debit_in_account_currency - credit_in_account_currency), 0) '
- 'ELSE ifnull(sum(credit_in_account_currency - debit_in_account_currency), 0) '
- 'END as outstanding_amount '
- 'FROM `tabGL Entry` WHERE (voucher_no=%s OR against_voucher=%s) '
- 'AND party_type IS NOT NULL '
- 'AND party_type != ""',
- (name, name), as_dict=1
- )
-
- outstanding_amount = res[0].get('outstanding_amount', 0) if res else 0
+ "SELECT "
+ 'CASE WHEN party_type IN ("Customer", "Student") '
+ "THEN ifnull(sum(debit_in_account_currency - credit_in_account_currency), 0) "
+ "ELSE ifnull(sum(credit_in_account_currency - debit_in_account_currency), 0) "
+ "END as outstanding_amount "
+ "FROM `tabGL Entry` WHERE (voucher_no=%s OR against_voucher=%s) "
+ "AND party_type IS NOT NULL "
+ 'AND party_type != ""',
+ (name, name),
+ as_dict=1,
+ )
+
+ outstanding_amount = res[0].get("outstanding_amount", 0) if res else 0
return outstanding_amount
@@ -1324,7 +1580,9 @@ def get_outstanding_on_journal_entry(name):
def get_reference_details(reference_doctype, reference_name, party_account_currency):
total_amount = outstanding_amount = exchange_rate = bill_no = 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 == "Fees":
total_amount = ref_doc.get("grand_total")
@@ -1337,20 +1595,22 @@ 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)
elif reference_doctype != "Journal Entry":
if ref_doc.doctype == "Expense Claim":
- total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
+ total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
elif ref_doc.doctype == "Employee Advance":
total_amount = ref_doc.advance_amount
exchange_rate = ref_doc.get("exchange_rate")
if party_account_currency != ref_doc.currency:
total_amount = flt(total_amount) * flt(exchange_rate)
elif ref_doc.doctype == "Gratuity":
- total_amount = ref_doc.amount
+ total_amount = ref_doc.amount
if not total_amount:
if party_account_currency == company_currency:
total_amount = ref_doc.base_grand_total
@@ -1360,16 +1620,21 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
if not exchange_rate:
# Get the exchange rate from the original ref doc
# or get it based on the posting date of the ref doc.
- exchange_rate = ref_doc.get("conversion_rate") or \
- get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
+ exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
+ party_account_currency, company_currency, ref_doc.posting_date
+ )
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = ref_doc.get("outstanding_amount")
bill_no = ref_doc.get("bill_no")
elif reference_doctype == "Expense Claim":
- outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\
- - flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount"))
+ outstanding_amount = (
+ flt(ref_doc.get("total_sanctioned_amount"))
+ + flt(ref_doc.get("total_taxes_and_charges"))
+ - flt(ref_doc.get("total_amount_reimbursed"))
+ - flt(ref_doc.get("total_advance_amount"))
+ )
elif reference_doctype == "Employee Advance":
- outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount))
+ outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
if party_account_currency != ref_doc.currency:
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
if party_account_currency == company_currency:
@@ -1380,18 +1645,22 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
else:
# Get the exchange rate based on the posting date of the ref doc.
- exchange_rate = get_exchange_rate(party_account_currency,
- company_currency, ref_doc.posting_date)
-
- return frappe._dict({
- "due_date": ref_doc.get("due_date"),
- "total_amount": flt(total_amount),
- "outstanding_amount": flt(outstanding_amount),
- "exchange_rate": flt(exchange_rate),
- "bill_no": bill_no
- })
-
-def get_amounts_based_on_reference_doctype(reference_doctype, ref_doc, party_account_currency, company_currency, reference_name):
+ exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
+
+ return frappe._dict(
+ {
+ "due_date": ref_doc.get("due_date"),
+ "total_amount": flt(total_amount),
+ "outstanding_amount": flt(outstanding_amount),
+ "exchange_rate": flt(exchange_rate),
+ "bill_no": bill_no,
+ }
+ )
+
+
+def get_amounts_based_on_reference_doctype(
+ reference_doctype, ref_doc, party_account_currency, company_currency, reference_name
+):
total_amount = outstanding_amount = exchange_rate = None
if reference_doctype == "Fees":
total_amount = ref_doc.get("grand_total")
@@ -1404,35 +1673,46 @@ def get_amounts_based_on_reference_doctype(reference_doctype, ref_doc, party_acc
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)
return total_amount, outstanding_amount, exchange_rate
-def get_amounts_based_on_ref_doc(reference_doctype, ref_doc, party_account_currency, company_currency):
+
+def get_amounts_based_on_ref_doc(
+ reference_doctype, ref_doc, party_account_currency, company_currency
+):
total_amount = outstanding_amount = exchange_rate = None
if ref_doc.doctype == "Expense Claim":
- total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
+ total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
elif ref_doc.doctype == "Employee Advance":
- total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc)
+ total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(
+ party_account_currency, ref_doc
+ )
if not total_amount:
total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
- party_account_currency, company_currency, ref_doc)
+ party_account_currency, company_currency, ref_doc
+ )
if not exchange_rate:
# Get the exchange rate from the original ref doc
# or get it based on the posting date of the ref doc
- exchange_rate = ref_doc.get("conversion_rate") or \
- get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
+ exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
+ party_account_currency, company_currency, ref_doc.posting_date
+ )
outstanding_amount, exchange_rate, bill_no = get_bill_no_and_update_amounts(
- reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency)
+ reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
+ )
return total_amount, outstanding_amount, exchange_rate, bill_no
+
def get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc):
total_amount = ref_doc.advance_amount
exchange_rate = ref_doc.get("exchange_rate")
@@ -1441,7 +1721,10 @@ def get_total_amount_exchange_rate_for_employee_advance(party_account_currency,
return total_amount, exchange_rate
-def get_total_amount_exchange_rate_base_on_currency(party_account_currency, company_currency, ref_doc):
+
+def get_total_amount_exchange_rate_base_on_currency(
+ party_account_currency, company_currency, ref_doc
+):
exchange_rate = None
if party_account_currency == company_currency:
total_amount = ref_doc.base_grand_total
@@ -1451,16 +1734,23 @@ def get_total_amount_exchange_rate_base_on_currency(party_account_currency, comp
return total_amount, exchange_rate
-def get_bill_no_and_update_amounts(reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency):
+
+def get_bill_no_and_update_amounts(
+ reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
+):
outstanding_amount = bill_no = None
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = ref_doc.get("outstanding_amount")
bill_no = ref_doc.get("bill_no")
elif reference_doctype == "Expense Claim":
- outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\
- - flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount"))
+ outstanding_amount = (
+ flt(ref_doc.get("total_sanctioned_amount"))
+ + flt(ref_doc.get("total_taxes_and_charges"))
+ - flt(ref_doc.get("total_amount_reimbursed"))
+ - flt(ref_doc.get("total_advance_amount"))
+ )
elif reference_doctype == "Employee Advance":
- outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount))
+ outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
if party_account_currency != ref_doc.currency:
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
if party_account_currency == company_currency:
@@ -1482,15 +1772,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
party_account = set_party_account(dt, dn, doc, party_type)
party_account_currency = set_party_account_currency(dt, party_account, doc)
payment_type = set_payment_type(dt, doc)
- grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc)
+ grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(
+ party_amount, dt, party_account_currency, doc
+ )
# bank or cash
bank = get_bank_cash_account(doc, bank_account)
paid_amount, received_amount = set_paid_amount_and_received_amount(
- dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc)
+ dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
+ )
- paid_amount, received_amount, discount_amount = apply_early_payment_discount(paid_amount, received_amount, doc)
+ paid_amount, received_amount, discount_amount = apply_early_payment_discount(
+ paid_amount, received_amount, doc
+ )
pe = frappe.new_doc("Payment Entry")
pe.payment_type = payment_type
@@ -1504,18 +1799,22 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
pe.contact_email = doc.get("contact_email")
pe.ensure_supplier_is_not_blocked()
- pe.paid_from = party_account if payment_type=="Receive" else bank.account
- pe.paid_to = party_account if payment_type=="Pay" else bank.account
- 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_from = party_account if payment_type == "Receive" else bank.account
+ pe.paid_to = party_account if payment_type == "Pay" else bank.account
+ 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_amount = paid_amount
pe.received_amount = received_amount
pe.letter_head = doc.get("letter_head")
- if dt in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice']:
- pe.project = (doc.get('project') or
- reduce(lambda prev,cur: prev or cur, [x.get('project') for x in doc.get('items')], None)) # get first non-empty project from items
+ if dt in ["Purchase Order", "Sales Order", "Sales Invoice", "Purchase Invoice"]:
+ pe.project = doc.get("project") or reduce(
+ lambda prev, cur: prev or cur, [x.get("project") for x in doc.get("items")], None
+ ) # get first non-empty project from items
if pe.party_type in ["Customer", "Supplier"]:
bank_account = get_party_bank_account(pe.party_type, pe.party)
@@ -1524,44 +1823,57 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
# only Purchase Invoice can be blocked individually
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
- frappe.msgprint(_('{0} is on hold till {1}').format(doc.name, doc.release_date))
+ frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date))
else:
- if (doc.doctype in ('Sales Invoice', 'Purchase Invoice')
- and frappe.get_value('Payment Terms Template',
- {'name': 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):
- pe.append('references', reference)
+ if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_value(
+ "Payment Terms Template",
+ {"name": 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
+ ):
+ pe.append("references", reference)
else:
if dt == "Dunning":
- pe.append("references", {
- 'reference_doctype': 'Sales Invoice',
- 'reference_name': doc.get('sales_invoice'),
- "bill_no": doc.get("bill_no"),
- "due_date": doc.get("due_date"),
- 'total_amount': doc.get('outstanding_amount'),
- 'outstanding_amount': doc.get('outstanding_amount'),
- 'allocated_amount': doc.get('outstanding_amount')
- })
- pe.append("references", {
- 'reference_doctype': dt,
- 'reference_name': dn,
- "bill_no": doc.get("bill_no"),
- "due_date": doc.get("due_date"),
- 'total_amount': doc.get('dunning_amount'),
- 'outstanding_amount': doc.get('dunning_amount'),
- 'allocated_amount': doc.get('dunning_amount')
- })
+ pe.append(
+ "references",
+ {
+ "reference_doctype": "Sales Invoice",
+ "reference_name": doc.get("sales_invoice"),
+ "bill_no": doc.get("bill_no"),
+ "due_date": doc.get("due_date"),
+ "total_amount": doc.get("outstanding_amount"),
+ "outstanding_amount": doc.get("outstanding_amount"),
+ "allocated_amount": doc.get("outstanding_amount"),
+ },
+ )
+ pe.append(
+ "references",
+ {
+ "reference_doctype": dt,
+ "reference_name": dn,
+ "bill_no": doc.get("bill_no"),
+ "due_date": doc.get("due_date"),
+ "total_amount": doc.get("dunning_amount"),
+ "outstanding_amount": doc.get("dunning_amount"),
+ "allocated_amount": doc.get("dunning_amount"),
+ },
+ )
else:
- pe.append("references", {
- 'reference_doctype': dt,
- 'reference_name': dn,
- "bill_no": doc.get("bill_no"),
- "due_date": doc.get("due_date"),
- 'total_amount': grand_total,
- 'outstanding_amount': outstanding_amount,
- 'allocated_amount': outstanding_amount
- })
+ pe.append(
+ "references",
+ {
+ "reference_doctype": dt,
+ "reference_name": dn,
+ "bill_no": doc.get("bill_no"),
+ "due_date": doc.get("due_date"),
+ "total_amount": grand_total,
+ "outstanding_amount": outstanding_amount,
+ "allocated_amount": outstanding_amount,
+ },
+ )
pe.setup_party_account_field()
pe.set_missing_values()
@@ -1572,25 +1884,32 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
pe.set_exchange_rate(ref_doc=reference_doc)
pe.set_amounts()
if discount_amount:
- pe.set_gain_or_loss(account_details={
- 'account': frappe.get_cached_value('Company', pe.company, "default_discount_account"),
- 'cost_center': pe.cost_center or frappe.get_cached_value('Company', pe.company, "cost_center"),
- 'amount': discount_amount * (-1 if payment_type == "Pay" else 1)
- })
+ pe.set_gain_or_loss(
+ account_details={
+ "account": frappe.get_cached_value("Company", pe.company, "default_discount_account"),
+ "cost_center": pe.cost_center
+ or frappe.get_cached_value("Company", pe.company, "cost_center"),
+ "amount": discount_amount * (-1 if payment_type == "Pay" else 1),
+ }
+ )
pe.set_difference_amount()
return pe
+
def get_bank_cash_account(doc, bank_account):
- bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"),
- account=bank_account)
+ bank = get_default_bank_cash_account(
+ doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
+ )
if not bank:
- bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"),
- account=bank_account)
+ bank = get_default_bank_cash_account(
+ doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
+ )
return bank
+
def set_party_type(dt):
if dt in ("Sales Invoice", "Sales Order", "Dunning"):
party_type = "Customer"
@@ -1602,6 +1921,7 @@ def set_party_type(dt):
party_type = "Student"
return party_type
+
def set_party_account(dt, dn, doc, party_type):
if dt == "Sales Invoice":
party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
@@ -1619,6 +1939,7 @@ def set_party_account(dt, dn, doc, party_type):
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
return party_account
+
def set_party_account_currency(dt, party_account, doc):
if dt not in ("Sales Invoice", "Purchase Invoice"):
party_account_currency = get_account_currency(party_account)
@@ -1626,14 +1947,18 @@ def set_party_account_currency(dt, party_account, doc):
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
return party_account_currency
+
def set_payment_type(dt, doc):
- if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \
- or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
- payment_type = "Receive"
+ if (
+ dt == "Sales Order"
+ or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)
+ ) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
+ payment_type = "Receive"
else:
payment_type = "Pay"
return payment_type
+
def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc):
grand_total = outstanding_amount = 0
if party_amount:
@@ -1646,8 +1971,7 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre
outstanding_amount = doc.outstanding_amount
elif dt in ("Expense Claim"):
grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges
- outstanding_amount = doc.grand_total \
- - doc.total_amount_reimbursed
+ outstanding_amount = doc.grand_total - doc.total_amount_reimbursed
elif dt == "Employee Advance":
grand_total = flt(doc.advance_amount)
outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
@@ -1671,7 +1995,10 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre
outstanding_amount = grand_total - flt(doc.advance_paid)
return grand_total, outstanding_amount
-def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc):
+
+def set_paid_amount_and_received_amount(
+ dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
+):
paid_amount = received_amount = 0
if party_account_currency == bank.account_currency:
paid_amount = received_amount = abs(outstanding_amount)
@@ -1680,37 +2007,38 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta
if bank_amount:
received_amount = bank_amount
else:
- received_amount = paid_amount * doc.get('conversion_rate', 1)
+ received_amount = paid_amount * doc.get("conversion_rate", 1)
if dt == "Employee Advance":
- received_amount = paid_amount * doc.get('exchange_rate', 1)
+ received_amount = paid_amount * doc.get("exchange_rate", 1)
else:
received_amount = abs(outstanding_amount)
if bank_amount:
paid_amount = bank_amount
else:
# if party account currency and bank currency is different then populate paid amount as well
- paid_amount = received_amount * doc.get('conversion_rate', 1)
+ paid_amount = received_amount * doc.get("conversion_rate", 1)
if dt == "Employee Advance":
- paid_amount = received_amount * doc.get('exchange_rate', 1)
+ paid_amount = received_amount * doc.get("exchange_rate", 1)
return paid_amount, received_amount
+
def apply_early_payment_discount(paid_amount, received_amount, doc):
total_discount = 0
- eligible_for_payments = ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice']
- has_payment_schedule = hasattr(doc, 'payment_schedule') and doc.payment_schedule
+ eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"]
+ has_payment_schedule = hasattr(doc, "payment_schedule") and doc.payment_schedule
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 getdate(nowdate()) <= term.discount_date:
- if term.discount_type == 'Percentage':
- discount_amount = flt(doc.get('grand_total')) * (term.discount / 100)
+ if term.discount_type == "Percentage":
+ discount_amount = flt(doc.get("grand_total")) * (term.discount / 100)
else:
discount_amount = term.discount
- discount_amount_in_foreign_currency = discount_amount * doc.get('conversion_rate', 1)
+ discount_amount_in_foreign_currency = discount_amount * doc.get("conversion_rate", 1)
- if doc.doctype == 'Sales Invoice':
+ if doc.doctype == "Sales Invoice":
paid_amount -= discount_amount
received_amount -= discount_amount_in_foreign_currency
else:
@@ -1720,38 +2048,46 @@ def apply_early_payment_discount(paid_amount, received_amount, doc):
total_discount += discount_amount
if total_discount:
- money = frappe.utils.fmt_money(total_discount, currency=doc.get('currency'))
+ money = frappe.utils.fmt_money(total_discount, currency=doc.get("currency"))
frappe.msgprint(_("Discount of {} applied as per Payment Term").format(money), alert=1)
return paid_amount, received_amount, total_discount
-def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
+
+def get_reference_as_per_payment_terms(
+ payment_schedule, dt, dn, doc, grand_total, outstanding_amount
+):
references = []
for payment_term in payment_schedule:
- payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount,
- payment_term.precision('payment_amount'))
+ payment_term_outstanding = flt(
+ payment_term.payment_amount - payment_term.paid_amount, payment_term.precision("payment_amount")
+ )
if payment_term_outstanding:
- references.append({
- 'reference_doctype': dt,
- 'reference_name': dn,
- 'bill_no': doc.get('bill_no'),
- 'due_date': doc.get('due_date'),
- 'total_amount': grand_total,
- 'outstanding_amount': outstanding_amount,
- 'payment_term': payment_term.payment_term,
- 'allocated_amount': payment_term_outstanding
- })
+ references.append(
+ {
+ "reference_doctype": dt,
+ "reference_name": dn,
+ "bill_no": doc.get("bill_no"),
+ "due_date": doc.get("due_date"),
+ "total_amount": grand_total,
+ "outstanding_amount": outstanding_amount,
+ "payment_term": payment_term.payment_term,
+ "allocated_amount": payment_term_outstanding,
+ }
+ )
return references
+
def get_paid_amount(dt, dn, party_type, party, account, due_date):
- if party_type=="Customer":
+ if party_type == "Customer":
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
else:
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
- paid_amount = frappe.db.sql("""
+ paid_amount = frappe.db.sql(
+ """
select ifnull(sum({dr_or_cr}), 0) as paid_amount
from `tabGL Entry`
where against_voucher_type = %s
@@ -1761,41 +2097,58 @@ 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))
+ """.format(
+ dr_or_cr=dr_or_cr
+ ),
+ (dt, dn, party_type, party, account, due_date),
+ )
return paid_amount[0][0] if paid_amount else 0
+
@frappe.whitelist()
-def get_party_and_account_balance(company, date, paid_from=None, paid_to=None, ptype=None, pty=None, cost_center=None):
- return frappe._dict({
- "party_balance": get_balance_on(party_type=ptype, party=pty, cost_center=cost_center),
- "paid_from_account_balance": get_balance_on(paid_from, date, cost_center=cost_center),
- "paid_to_account_balance": get_balance_on(paid_to, date=date, cost_center=cost_center)
- })
+def get_party_and_account_balance(
+ company, date, paid_from=None, paid_to=None, ptype=None, pty=None, cost_center=None
+):
+ return frappe._dict(
+ {
+ "party_balance": get_balance_on(party_type=ptype, party=pty, cost_center=cost_center),
+ "paid_from_account_balance": get_balance_on(paid_from, date, cost_center=cost_center),
+ "paid_to_account_balance": get_balance_on(paid_to, date=date, cost_center=cost_center),
+ }
+ )
+
@frappe.whitelist()
def make_payment_order(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
+
def set_missing_values(source, target):
target.payment_order_type = "Payment Entry"
- target.append('references', dict(
- reference_doctype="Payment Entry",
- reference_name=source.name,
- bank_account=source.party_bank_account,
- amount=source.paid_amount,
- account=source.paid_to,
- supplier=source.party,
- mode_of_payment=source.mode_of_payment,
- ))
-
- doclist = get_mapped_doc("Payment Entry", source_name, {
- "Payment Entry": {
- "doctype": "Payment Order",
- "validation": {
- "docstatus": ["=", 1]
- },
- }
+ target.append(
+ "references",
+ dict(
+ reference_doctype="Payment Entry",
+ reference_name=source.name,
+ bank_account=source.party_bank_account,
+ amount=source.paid_amount,
+ account=source.paid_to,
+ supplier=source.party,
+ mode_of_payment=source.mode_of_payment,
+ ),
+ )
- }, target_doc, set_missing_values)
+ doclist = get_mapped_doc(
+ "Payment Entry",
+ source_name,
+ {
+ "Payment Entry": {
+ "doctype": "Payment Order",
+ "validation": {"docstatus": ["=", 1]},
+ }
+ },
+ target_doc,
+ set_missing_values,
+ )
return doclist
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 349b8bb5b1b1..5b70b510d2b4 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -32,10 +32,9 @@ def test_payment_entry_against_order(self):
pe.insert()
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- ["Debtors - _TC", 0, 1000, so.name],
- ["_Test Cash - _TC", 1000.0, 0, None]
- ])
+ expected_gle = dict(
+ (d[0], d) for d in [["Debtors - _TC", 0, 1000, so.name], ["_Test Cash - _TC", 1000.0, 0, None]]
+ )
self.validate_gl_entries(pe.name, expected_gle)
@@ -48,9 +47,9 @@ def test_payment_entry_against_order(self):
self.assertEqual(so_advance_paid, 0)
def test_payment_entry_for_blocked_supplier_invoice(self):
- supplier = frappe.get_doc('Supplier', '_Test Supplier')
+ supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
- supplier.hold_type = 'Invoices'
+ supplier.hold_type = "Invoices"
supplier.save()
self.assertRaises(frappe.ValidationError, make_purchase_invoice)
@@ -59,32 +58,40 @@ def test_payment_entry_for_blocked_supplier_invoice(self):
supplier.save()
def test_payment_entry_for_blocked_supplier_payments(self):
- supplier = frappe.get_doc('Supplier', '_Test Supplier')
+ supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
- supplier.hold_type = 'Payments'
+ supplier.hold_type = "Payments"
supplier.save()
pi = make_purchase_invoice()
self.assertRaises(
- frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name,
- bank_account="_Test Bank - _TC")
+ frappe.ValidationError,
+ get_payment_entry,
+ dt="Purchase Invoice",
+ dn=pi.name,
+ bank_account="_Test Bank - _TC",
+ )
supplier.on_hold = 0
supplier.save()
def test_payment_entry_for_blocked_supplier_payments_today_date(self):
- supplier = frappe.get_doc('Supplier', '_Test Supplier')
+ supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
- supplier.hold_type = 'Payments'
+ supplier.hold_type = "Payments"
supplier.release_date = nowdate()
supplier.save()
pi = make_purchase_invoice()
self.assertRaises(
- frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name,
- bank_account="_Test Bank - _TC")
+ frappe.ValidationError,
+ get_payment_entry,
+ dt="Purchase Invoice",
+ dn=pi.name,
+ bank_account="_Test Bank - _TC",
+ )
supplier.on_hold = 0
supplier.save()
@@ -93,15 +100,15 @@ def test_payment_entry_for_blocked_supplier_payments_past_date(self):
# this test is meant to fail only if something fails in the try block
with self.assertRaises(Exception):
try:
- supplier = frappe.get_doc('Supplier', '_Test Supplier')
+ supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
- supplier.hold_type = 'Payments'
- supplier.release_date = '2018-03-01'
+ supplier.hold_type = "Payments"
+ supplier.release_date = "2018-03-01"
supplier.save()
pi = make_purchase_invoice()
- get_payment_entry('Purchase Invoice', pi.name, bank_account="_Test Bank - _TC")
+ get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
supplier.on_hold = 0
supplier.save()
@@ -111,8 +118,12 @@ def test_payment_entry_for_blocked_supplier_payments_past_date(self):
raise Exception
def test_payment_entry_against_si_usd_to_usd(self):
- si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
- currency="USD", conversion_rate=50)
+ si = create_sales_invoice(
+ customer="_Test Customer USD",
+ debit_to="_Test Receivable USD - _TC",
+ currency="USD",
+ conversion_rate=50,
+ )
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
@@ -120,10 +131,13 @@ def test_payment_entry_against_si_usd_to_usd(self):
pe.insert()
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- ["_Test Receivable USD - _TC", 0, 5000, si.name],
- ["_Test Bank USD - _TC", 5000.0, 0, None]
- ])
+ expected_gle = dict(
+ (d[0], d)
+ for d in [
+ ["_Test Receivable USD - _TC", 0, 5000, si.name],
+ ["_Test Bank USD - _TC", 5000.0, 0, None],
+ ]
+ )
self.validate_gl_entries(pe.name, expected_gle)
@@ -136,8 +150,12 @@ def test_payment_entry_against_si_usd_to_usd(self):
self.assertEqual(outstanding_amount, 100)
def test_payment_entry_against_pi(self):
- pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
- currency="USD", conversion_rate=50)
+ pi = make_purchase_invoice(
+ supplier="_Test Supplier USD",
+ debit_to="_Test Payable USD - _TC",
+ currency="USD",
+ conversion_rate=50,
+ )
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
@@ -145,20 +163,26 @@ def test_payment_entry_against_pi(self):
pe.insert()
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- ["_Test Payable USD - _TC", 12500, 0, pi.name],
- ["_Test Bank USD - _TC", 0, 12500, None]
- ])
+ expected_gle = dict(
+ (d[0], d)
+ for d in [
+ ["_Test Payable USD - _TC", 12500, 0, pi.name],
+ ["_Test Bank USD - _TC", 0, 12500, None],
+ ]
+ )
self.validate_gl_entries(pe.name, expected_gle)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 0)
-
def test_payment_against_sales_invoice_to_check_status(self):
- si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
- currency="USD", conversion_rate=50)
+ si = create_sales_invoice(
+ customer="_Test Customer USD",
+ debit_to="_Test Receivable USD - _TC",
+ currency="USD",
+ conversion_rate=50,
+ )
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
@@ -167,28 +191,35 @@ def test_payment_against_sales_invoice_to_check_status(self):
pe.insert()
pe.submit()
- outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
+ outstanding_amount, status = frappe.db.get_value(
+ "Sales Invoice", si.name, ["outstanding_amount", "status"]
+ )
self.assertEqual(flt(outstanding_amount), 0)
- self.assertEqual(status, 'Paid')
+ self.assertEqual(status, "Paid")
pe.cancel()
- outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
+ outstanding_amount, status = frappe.db.get_value(
+ "Sales Invoice", si.name, ["outstanding_amount", "status"]
+ )
self.assertEqual(flt(outstanding_amount), 100)
- self.assertEqual(status, 'Unpaid')
+ self.assertEqual(status, "Unpaid")
def test_payment_entry_against_payment_terms(self):
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
create_payment_terms_template()
- si.payment_terms_template = 'Test Receivable Template'
-
- si.append('taxes', {
- "charge_type": "On Net Total",
- "account_head": "_Test Account Service Tax - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Service Tax",
- "rate": 18
- })
+ si.payment_terms_template = "Test Receivable Template"
+
+ si.append(
+ "taxes",
+ {
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Service Tax - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Service Tax",
+ "rate": 18,
+ },
+ )
si.save()
si.submit()
@@ -197,25 +228,28 @@ def test_payment_entry_against_payment_terms(self):
pe.submit()
si.load_from_db()
- self.assertEqual(pe.references[0].payment_term, 'Basic Amount Receivable')
- self.assertEqual(pe.references[1].payment_term, 'Tax Receivable')
+ self.assertEqual(pe.references[0].payment_term, "Basic Amount Receivable")
+ self.assertEqual(pe.references[1].payment_term, "Tax Receivable")
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
def test_payment_entry_against_payment_terms_with_discount(self):
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
create_payment_terms_template_with_discount()
- si.payment_terms_template = 'Test Discount Template'
+ si.payment_terms_template = "Test Discount Template"
- frappe.db.set_value('Company', si.company, 'default_discount_account', 'Write Off - _TC')
+ frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC")
- si.append('taxes', {
- "charge_type": "On Net Total",
- "account_head": "_Test Account Service Tax - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "description": "Service Tax",
- "rate": 18
- })
+ si.append(
+ "taxes",
+ {
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Service Tax - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Service Tax",
+ "rate": 18,
+ },
+ )
si.save()
si.submit()
@@ -224,16 +258,19 @@ def test_payment_entry_against_payment_terms_with_discount(self):
pe.submit()
si.load_from_db()
- self.assertEqual(pe.references[0].payment_term, '30 Credit Days with 10% Discount')
+ self.assertEqual(pe.references[0].payment_term, "30 Credit Days with 10% Discount")
self.assertEqual(si.payment_schedule[0].payment_amount, 236.0)
self.assertEqual(si.payment_schedule[0].paid_amount, 212.40)
self.assertEqual(si.payment_schedule[0].outstanding, 0)
self.assertEqual(si.payment_schedule[0].discounted_amount, 23.6)
-
def test_payment_against_purchase_invoice_to_check_status(self):
- pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
- currency="USD", conversion_rate=50)
+ pi = make_purchase_invoice(
+ supplier="_Test Supplier USD",
+ debit_to="_Test Payable USD - _TC",
+ currency="USD",
+ conversion_rate=50,
+ )
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
@@ -242,21 +279,27 @@ def test_payment_against_purchase_invoice_to_check_status(self):
pe.insert()
pe.submit()
- outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
+ outstanding_amount, status = frappe.db.get_value(
+ "Purchase Invoice", pi.name, ["outstanding_amount", "status"]
+ )
self.assertEqual(flt(outstanding_amount), 0)
- self.assertEqual(status, 'Paid')
+ self.assertEqual(status, "Paid")
pe.cancel()
- outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
+ outstanding_amount, status = frappe.db.get_value(
+ "Purchase Invoice", pi.name, ["outstanding_amount", "status"]
+ )
self.assertEqual(flt(outstanding_amount), 250)
- self.assertEqual(status, 'Unpaid')
+ self.assertEqual(status, "Unpaid")
def test_payment_entry_against_ec(self):
- payable = frappe.get_cached_value('Company', "_Test Company", 'default_payable_account')
+ payable = frappe.get_cached_value("Company", "_Test Company", "default_payable_account")
ec = make_expense_claim(payable, 300, 300, "_Test Company", "Travel Expenses - _TC")
- pe = get_payment_entry("Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300)
+ pe = get_payment_entry(
+ "Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300
+ )
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
pe.source_exchange_rate = 1
@@ -264,68 +307,87 @@ def test_payment_entry_against_ec(self):
pe.insert()
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- [payable, 300, 0, ec.name],
- ["_Test Bank USD - _TC", 0, 300, None]
- ])
+ expected_gle = dict(
+ (d[0], d) for d in [[payable, 300, 0, ec.name], ["_Test Bank USD - _TC", 0, 300, None]]
+ )
self.validate_gl_entries(pe.name, expected_gle)
- outstanding_amount = flt(frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")) - \
- flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
+ outstanding_amount = flt(
+ frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")
+ ) - flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
self.assertEqual(outstanding_amount, 0)
def test_payment_entry_against_si_usd_to_inr(self):
- si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
- currency="USD", conversion_rate=50)
- pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
- bank_account="_Test Bank - _TC", bank_amount=900)
+ si = create_sales_invoice(
+ customer="_Test Customer USD",
+ debit_to="_Test Receivable USD - _TC",
+ currency="USD",
+ conversion_rate=50,
+ )
+ pe = get_payment_entry(
+ "Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank - _TC", bank_amount=900
+ )
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
self.assertEqual(pe.difference_amount, 100)
- pe.append("deductions", {
- "account": "_Test Exchange Gain/Loss - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "amount": 100
- })
+ pe.append(
+ "deductions",
+ {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 100,
+ },
+ )
pe.insert()
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- ["_Test Receivable USD - _TC", 0, 1000, si.name],
- ["_Test Bank - _TC", 900, 0, None],
- ["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
- ])
+ expected_gle = dict(
+ (d[0], d)
+ for d in [
+ ["_Test Receivable USD - _TC", 0, 1000, si.name],
+ ["_Test Bank - _TC", 900, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
+ ]
+ )
self.validate_gl_entries(pe.name, expected_gle)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 80)
- def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency (self):
- si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
- currency="USD", conversion_rate=50, do_not_save=1)
+ def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency(self):
+ si = create_sales_invoice(
+ customer="_Test Customer USD",
+ debit_to="_Test Receivable USD - _TC",
+ currency="USD",
+ conversion_rate=50,
+ do_not_save=1,
+ )
si.plc_conversion_rate = 50
si.save()
si.submit()
- pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
- bank_account="_Test Bank USD - _TC", bank_amount=900)
+ pe = get_payment_entry(
+ "Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank USD - _TC", bank_amount=900
+ )
pe.source_exchange_rate = 45.263
pe.target_exchange_rate = 45.263
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
-
- pe.append("deductions", {
- "account": "_Test Exchange Gain/Loss - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "amount": 94.80
- })
+ pe.append(
+ "deductions",
+ {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 94.80,
+ },
+ )
pe.save()
@@ -359,8 +421,7 @@ def test_payment_entry_retrieves_last_exchange_rate(self):
pe.set_amounts()
self.assertEqual(
- pe.source_exchange_rate, 65.1,
- "{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
+ pe.source_exchange_rate, 65.1, "{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
)
def test_internal_transfer_usd_to_inr(self):
@@ -382,20 +443,26 @@ def test_internal_transfer_usd_to_inr(self):
self.assertEqual(pe.difference_amount, 500)
- pe.append("deductions", {
- "account": "_Test Exchange Gain/Loss - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "amount": 500
- })
+ pe.append(
+ "deductions",
+ {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 500,
+ },
+ )
pe.insert()
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- ["_Test Bank USD - _TC", 0, 5000, None],
- ["_Test Bank - _TC", 4500, 0, None],
- ["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
- ])
+ expected_gle = dict(
+ (d[0], d)
+ for d in [
+ ["_Test Bank USD - _TC", 0, 5000, None],
+ ["_Test Bank - _TC", 4500, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
+ ]
+ )
self.validate_gl_entries(pe.name, expected_gle)
@@ -435,10 +502,9 @@ def test_payment_against_negative_sales_invoice(self):
pe3.insert()
pe3.submit()
- expected_gle = dict((d[0], d) for d in [
- ["Debtors - _TC", 100, 0, si1.name],
- ["_Test Cash - _TC", 0, 100, None]
- ])
+ expected_gle = dict(
+ (d[0], d) for d in [["Debtors - _TC", 100, 0, si1.name], ["_Test Cash - _TC", 0, 100, None]]
+ )
self.validate_gl_entries(pe3.name, expected_gle)
@@ -462,12 +528,16 @@ def validate_gl_entries(self, voucher_no, expected_gle):
self.assertEqual(expected_gle[gle.account][3], gle.against_voucher)
def get_gle(self, voucher_no):
- return frappe.db.sql("""select account, debit, credit, against_voucher
+ return frappe.db.sql(
+ """select account, debit, credit, against_voucher
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
- order by account asc""", voucher_no, as_dict=1)
+ order by account asc""",
+ voucher_no,
+ as_dict=1,
+ )
def test_payment_entry_write_off_difference(self):
- si = create_sales_invoice()
+ si = create_sales_invoice()
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
@@ -477,11 +547,10 @@ def test_payment_entry_write_off_difference(self):
self.assertEqual(pe.unallocated_amount, 10)
pe.received_amount = pe.paid_amount = 95
- pe.append("deductions", {
- "account": "_Test Write Off - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "amount": 5
- })
+ pe.append(
+ "deductions",
+ {"account": "_Test Write Off - _TC", "cost_center": "_Test Cost Center - _TC", "amount": 5},
+ )
pe.save()
self.assertEqual(pe.unallocated_amount, 0)
@@ -489,27 +558,37 @@ def test_payment_entry_write_off_difference(self):
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- ["Debtors - _TC", 0, 100, si.name],
- ["_Test Cash - _TC", 95, 0, None],
- ["_Test Write Off - _TC", 5, 0, None]
- ])
+ expected_gle = dict(
+ (d[0], d)
+ for d in [
+ ["Debtors - _TC", 0, 100, si.name],
+ ["_Test Cash - _TC", 95, 0, None],
+ ["_Test Write Off - _TC", 5, 0, None],
+ ]
+ )
self.validate_gl_entries(pe.name, expected_gle)
def test_payment_entry_exchange_gain_loss(self):
- si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
- currency="USD", conversion_rate=50)
+ si = create_sales_invoice(
+ customer="_Test Customer USD",
+ debit_to="_Test Receivable USD - _TC",
+ currency="USD",
+ conversion_rate=50,
+ )
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
pe.source_exchange_rate = 55
- pe.append("deductions", {
- "account": "_Test Exchange Gain/Loss - _TC",
- "cost_center": "_Test Cost Center - _TC",
- "amount": -500
- })
+ pe.append(
+ "deductions",
+ {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": -500,
+ },
+ )
pe.save()
self.assertEqual(pe.unallocated_amount, 0)
@@ -517,11 +596,14 @@ def test_payment_entry_exchange_gain_loss(self):
pe.submit()
- expected_gle = dict((d[0], d) for d in [
- ["_Test Receivable USD - _TC", 0, 5000, si.name],
- ["_Test Bank USD - _TC", 5500, 0, None],
- ["_Test Exchange Gain/Loss - _TC", 0, 500, None],
- ])
+ expected_gle = dict(
+ (d[0], d)
+ for d in [
+ ["_Test Receivable USD - _TC", 0, 5000, si.name],
+ ["_Test Bank USD - _TC", 5500, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 0, 500, None],
+ ]
+ )
self.validate_gl_entries(pe.name, expected_gle)
@@ -530,10 +612,11 @@ def test_payment_entry_exchange_gain_loss(self):
def test_payment_entry_against_sales_invoice_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
- si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
+ si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
self.assertEqual(pe.cost_center, si.cost_center)
@@ -546,18 +629,18 @@ def test_payment_entry_against_sales_invoice_with_cost_centre(self):
pe.submit()
expected_values = {
- "_Test Bank - _TC": {
- "cost_center": cost_center
- },
- "Debtors - _TC": {
- "cost_center": cost_center
- }
+ "_Test Bank - _TC": {"cost_center": cost_center},
+ "Debtors - _TC": {"cost_center": cost_center},
}
- gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+ gl_entries = frappe.db.sql(
+ """select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
- order by account asc""", pe.name, as_dict=1)
+ order by account asc""",
+ pe.name,
+ as_dict=1,
+ )
self.assertTrue(gl_entries)
@@ -566,10 +649,13 @@ def test_payment_entry_against_sales_invoice_with_cost_centre(self):
def test_payment_entry_against_purchase_invoice_with_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
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)
@@ -582,18 +668,18 @@ def test_payment_entry_against_purchase_invoice_with_cost_center(self):
pe.submit()
expected_values = {
- "_Test Bank - _TC": {
- "cost_center": cost_center
- },
- "Creditors - _TC": {
- "cost_center": cost_center
- }
+ "_Test Bank - _TC": {"cost_center": cost_center},
+ "Creditors - _TC": {"cost_center": cost_center},
}
- gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+ gl_entries = frappe.db.sql(
+ """select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
- order by account asc""", pe.name, as_dict=1)
+ order by account asc""",
+ pe.name,
+ as_dict=1,
+ )
self.assertTrue(gl_entries)
@@ -603,13 +689,16 @@ def test_payment_entry_against_purchase_invoice_with_cost_center(self):
def test_payment_entry_account_and_party_balance_with_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_balance_on
+
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
- si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
+ 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")
@@ -634,94 +723,109 @@ def test_payment_entry_account_and_party_balance_with_cost_center(self):
self.assertEqual(flt(expected_party_account_balance), party_account_balance)
def test_multi_currency_payment_entry_with_taxes(self):
- payment_entry = create_payment_entry(party='_Test Supplier USD', paid_to = '_Test Payable USD - _TC',
- save=True)
- payment_entry.append('taxes', {
- 'account_head': '_Test Account Service Tax - _TC',
- 'charge_type': 'Actual',
- 'tax_amount': 10,
- 'add_deduct_tax': 'Add',
- 'description': 'Test'
- })
+ payment_entry = create_payment_entry(
+ party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True
+ )
+ payment_entry.append(
+ "taxes",
+ {
+ "account_head": "_Test Account Service Tax - _TC",
+ "charge_type": "Actual",
+ "tax_amount": 10,
+ "add_deduct_tax": "Add",
+ "description": "Test",
+ },
+ )
payment_entry.save()
self.assertEqual(payment_entry.base_total_taxes_and_charges, 10)
- self.assertEqual(flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2))
+ self.assertEqual(
+ flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2)
+ )
+
def create_payment_entry(**args):
- payment_entry = frappe.new_doc('Payment Entry')
- payment_entry.company = args.get('company') or '_Test Company'
- payment_entry.payment_type = args.get('payment_type') or 'Pay'
- payment_entry.party_type = args.get('party_type') or 'Supplier'
- payment_entry.party = args.get('party') or '_Test Supplier'
- payment_entry.paid_from = args.get('paid_from') or '_Test Bank - _TC'
- payment_entry.paid_to = args.get('paid_to') or 'Creditors - _TC'
- payment_entry.paid_amount = args.get('paid_amount') or 1000
+ payment_entry = frappe.new_doc("Payment Entry")
+ payment_entry.company = args.get("company") or "_Test Company"
+ payment_entry.payment_type = args.get("payment_type") or "Pay"
+ payment_entry.party_type = args.get("party_type") or "Supplier"
+ payment_entry.party = args.get("party") or "_Test Supplier"
+ payment_entry.paid_from = args.get("paid_from") or "_Test Bank - _TC"
+ payment_entry.paid_to = args.get("paid_to") or "Creditors - _TC"
+ payment_entry.paid_amount = args.get("paid_amount") or 1000
payment_entry.setup_party_account_field()
payment_entry.set_missing_values()
payment_entry.set_exchange_rate()
payment_entry.received_amount = payment_entry.paid_amount / payment_entry.target_exchange_rate
- payment_entry.reference_no = 'Test001'
+ payment_entry.reference_no = "Test001"
payment_entry.reference_date = nowdate()
- if args.get('save'):
+ if args.get("save"):
payment_entry.save()
- if args.get('submit'):
+ if args.get("submit"):
payment_entry.submit()
return payment_entry
+
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({
- 'doctype': 'Payment Terms Template',
- 'template_name': 'Test Receivable Template',
- 'allocate_payment_based_on_payment_terms': 1,
- 'terms': [{
- 'doctype': 'Payment Terms Template Detail',
- 'payment_term': 'Basic Amount Receivable',
- 'invoice_portion': 84.746,
- 'credit_days_based_on': 'Day(s) after invoice date',
- 'credit_days': 1
- },
+ 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(
{
- 'doctype': 'Payment Terms Template Detail',
- 'payment_term': 'Tax Receivable',
- 'invoice_portion': 15.254,
- 'credit_days_based_on': 'Day(s) after invoice date',
- 'credit_days': 2
- }]
- }).insert()
+ "doctype": "Payment Terms Template",
+ "template_name": "Test Receivable Template",
+ "allocate_payment_based_on_payment_terms": 1,
+ "terms": [
+ {
+ "doctype": "Payment Terms Template Detail",
+ "payment_term": "Basic Amount Receivable",
+ "invoice_portion": 84.746,
+ "credit_days_based_on": "Day(s) after invoice date",
+ "credit_days": 1,
+ },
+ {
+ "doctype": "Payment Terms Template Detail",
+ "payment_term": "Tax Receivable",
+ "invoice_portion": 15.254,
+ "credit_days_based_on": "Day(s) after invoice date",
+ "credit_days": 2,
+ },
+ ],
+ }
+ ).insert()
+
def create_payment_terms_template_with_discount():
- create_payment_term('30 Credit Days with 10% Discount')
-
- if not frappe.db.exists('Payment Terms Template', 'Test Discount Template'):
- payment_term_template = frappe.get_doc({
- 'doctype': 'Payment Terms Template',
- 'template_name': 'Test Discount Template',
- 'allocate_payment_based_on_payment_terms': 1,
- 'terms': [{
- 'doctype': 'Payment Terms Template Detail',
- 'payment_term': '30 Credit Days with 10% Discount',
- 'invoice_portion': 100,
- 'credit_days_based_on': 'Day(s) after invoice date',
- 'credit_days': 2,
- 'discount': 10,
- 'discount_validity_based_on': 'Day(s) after invoice date',
- 'discount_validity': 1
- }]
- }).insert()
+ create_payment_term("30 Credit Days with 10% Discount")
+
+ if not frappe.db.exists("Payment Terms Template", "Test Discount Template"):
+ payment_term_template = frappe.get_doc(
+ {
+ "doctype": "Payment Terms Template",
+ "template_name": "Test Discount Template",
+ "allocate_payment_based_on_payment_terms": 1,
+ "terms": [
+ {
+ "doctype": "Payment Terms Template Detail",
+ "payment_term": "30 Credit Days with 10% Discount",
+ "invoice_portion": 100,
+ "credit_days_based_on": "Day(s) after invoice date",
+ "credit_days": 2,
+ "discount": 10,
+ "discount_validity_based_on": "Day(s) after invoice date",
+ "discount_validity": 1,
+ }
+ ],
+ }
+ ).insert()
+
def create_payment_term(name):
- if not frappe.db.exists('Payment Term', name):
- frappe.get_doc({
- 'doctype': 'Payment Term',
- 'payment_term_name': name
- }).insert()
+ if not frappe.db.exists("Payment Term", name):
+ frappe.get_doc({"doctype": "Payment Term", "payment_term_name": name}).insert()
diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
index 25dc4e6a60d7..ab47b6151cd1 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
@@ -18,10 +18,13 @@ def validate(self):
def update_default_payment_gateway(self):
if self.is_default:
- frappe.db.sql("""update `tabPayment Gateway Account` set is_default = 0
- where is_default = 1 """)
+ frappe.db.sql(
+ """update `tabPayment Gateway Account` set is_default = 0
+ where is_default = 1 """
+ )
def set_as_default_if_not_set(self):
- if not frappe.db.get_value("Payment Gateway Account",
- {"is_default": 1, "name": ("!=", self.name)}, "name"):
+ if not frappe.db.get_value(
+ "Payment Gateway Account", {"is_default": 1, "name": ("!=", self.name)}, "name"
+ ):
self.is_default = 1
diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py
index 39968922417e..d0aaee88350c 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py
@@ -1,15 +1,6 @@
def get_data():
return {
- 'fieldname': 'payment_gateway_account',
- 'non_standard_fieldnames': {
- 'Subscription Plan': 'payment_gateway'
- },
- 'transactions': [
- {
- 'items': ['Payment Request']
- },
- {
- 'items': ['Subscription Plan']
- }
- ]
+ "fieldname": "payment_gateway_account",
+ "non_standard_fieldnames": {"Subscription Plan": "payment_gateway"},
+ "transactions": [{"items": ["Payment Request"]}, {"items": ["Subscription Plan"]}],
}
diff --git a/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py
index 1895c12ad7c3..7a8cdf733556 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py
@@ -5,5 +5,6 @@
# test_records = frappe.get_test_records('Payment Gateway Account')
+
class TestPaymentGatewayAccount(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js
index 9074defa577e..7d85d89c452d 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.js
+++ b/erpnext/accounts/doctype/payment_order/payment_order.js
@@ -12,7 +12,6 @@ frappe.ui.form.on('Payment Order', {
});
frm.set_df_property('references', 'cannot_add_rows', true);
- frm.set_df_property('references', 'cannot_delete_rows', true);
},
refresh: function(frm) {
if (frm.doc.docstatus == 0) {
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py
index 50a58b8a0ab2..3c45d20770d4 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order.py
@@ -18,9 +18,9 @@ def on_cancel(self):
self.update_payment_status(cancel=True)
def update_payment_status(self, cancel=False):
- status = 'Payment Ordered'
+ status = "Payment Ordered"
if cancel:
- status = 'Initiated'
+ status = "Initiated"
if self.payment_order_type == "Payment Request":
ref_field = "status"
@@ -32,67 +32,67 @@ def update_payment_status(self, cancel=False):
for d in self.references:
frappe.db.set_value(self.payment_order_type, d.get(ref_doc_field), ref_field, status)
+
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference`
+ return frappe.db.sql(
+ """ select mode_of_payment from `tabPayment Order Reference`
where parent = %(parent)s and mode_of_payment like %(txt)s
- limit %(start)s, %(page_len)s""", {
- 'parent': filters.get("parent"),
- 'start': start,
- 'page_len': page_len,
- 'txt': "%%%s%%" % txt
- })
+ limit %(start)s, %(page_len)s""",
+ {"parent": filters.get("parent"), "start": start, "page_len": page_len, "txt": "%%%s%%" % txt},
+ )
+
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.sql(""" select supplier from `tabPayment Order Reference`
+ return frappe.db.sql(
+ """ select supplier from `tabPayment Order Reference`
where parent = %(parent)s and supplier like %(txt)s and
(payment_reference is null or payment_reference='')
- limit %(start)s, %(page_len)s""", {
- 'parent': filters.get("parent"),
- 'start': start,
- 'page_len': page_len,
- 'txt': "%%%s%%" % txt
- })
+ limit %(start)s, %(page_len)s""",
+ {"parent": filters.get("parent"), "start": start, "page_len": page_len, "txt": "%%%s%%" % txt},
+ )
+
@frappe.whitelist()
def make_payment_records(name, supplier, mode_of_payment=None):
- doc = frappe.get_doc('Payment Order', name)
+ doc = frappe.get_doc("Payment Order", name)
make_journal_entry(doc, supplier, mode_of_payment)
+
def make_journal_entry(doc, supplier, mode_of_payment=None):
- je = frappe.new_doc('Journal Entry')
+ 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':
+ je.voucher_type = "Bank Entry"
+ if mode_of_payment and mode_of_payment_type.get(mode_of_payment) == "Cash":
je.voucher_type = "Cash Entry"
paid_amt = 0
- party_account = get_party_account('Supplier', supplier, doc.company)
+ party_account = get_party_account("Supplier", supplier, doc.company)
for d in doc.references:
- if (d.supplier == supplier
- and (not mode_of_payment or mode_of_payment == d.mode_of_payment)):
- je.append('accounts', {
- 'account': party_account,
- 'debit_in_account_currency': d.amount,
- 'party_type': 'Supplier',
- 'party': supplier,
- 'reference_type': d.reference_doctype,
- 'reference_name': d.reference_name
- })
+ if d.supplier == supplier and (not mode_of_payment or mode_of_payment == d.mode_of_payment):
+ je.append(
+ "accounts",
+ {
+ "account": party_account,
+ "debit_in_account_currency": d.amount,
+ "party_type": "Supplier",
+ "party": supplier,
+ "reference_type": d.reference_doctype,
+ "reference_name": d.reference_name,
+ },
+ )
paid_amt += d.amount
- je.append('accounts', {
- 'account': doc.account,
- 'credit_in_account_currency': paid_amt
- })
+ je.append("accounts", {"account": doc.account, "credit_in_account_currency": paid_amt})
je.flags.ignore_mandatory = True
je.save()
diff --git a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
index 37bbaec33d9d..f82886e7c26a 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
@@ -1,9 +1,5 @@
def get_data():
return {
- 'fieldname': 'payment_order',
- 'transactions': [
- {
- 'items': ['Payment Entry', 'Journal Entry']
- }
- ]
+ "fieldname": "payment_order",
+ "transactions": [{"items": ["Payment Entry", "Journal Entry"]}],
}
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py
index 3f4d89b4eaf9..0dcb1794b9ad 100644
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py
@@ -26,7 +26,9 @@ def tearDown(self):
def test_payment_order_creation_against_payment_entry(self):
purchase_invoice = make_purchase_invoice()
- payment_entry = get_payment_entry("Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC")
+ payment_entry = get_payment_entry(
+ "Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC"
+ )
payment_entry.reference_no = "_Test_Payment_Order"
payment_entry.reference_date = getdate()
payment_entry.party_bank_account = "Checking Account - Citi Bank"
@@ -40,13 +42,16 @@ def test_payment_order_creation_against_payment_entry(self):
self.assertEqual(reference_doc.supplier, "_Test Supplier")
self.assertEqual(reference_doc.amount, 250)
+
def create_payment_order_against_payment_entry(ref_doc, order_type):
- payment_order = frappe.get_doc(dict(
- doctype="Payment Order",
- company="_Test Company",
- payment_order_type=order_type,
- company_bank_account="Checking Account - Citi Bank"
- ))
+ payment_order = frappe.get_doc(
+ dict(
+ doctype="Payment Order",
+ company="_Test Company",
+ payment_order_type=order_type,
+ company_bank_account="Checking Account - Citi Bank",
+ )
+ )
doc = make_payment_order(ref_doc.name, payment_order)
doc.save()
doc.submit()
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 548571d1d765..907b76915af7 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -32,30 +32,43 @@ def get_nonreconciled_payment_entries(self):
non_reconciled_payments = payment_entries + journal_entries + dr_or_cr_notes
if self.payment_limit:
- non_reconciled_payments = non_reconciled_payments[:self.payment_limit]
+ non_reconciled_payments = non_reconciled_payments[: self.payment_limit]
- non_reconciled_payments = sorted(non_reconciled_payments, key=lambda k: k['posting_date'] or getdate(nowdate()))
+ non_reconciled_payments = sorted(
+ non_reconciled_payments, key=lambda k: k["posting_date"] or getdate(nowdate())
+ )
self.add_payment_entries(non_reconciled_payments)
def get_payment_entries(self):
- order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
+ order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order"
condition = self.get_conditions(get_payments=True)
- payment_entries = get_advance_payment_entries(self.party_type, self.party,
- self.receivable_payable_account, order_doctype, against_all_orders=True, limit=self.payment_limit,
- condition=condition)
+ payment_entries = get_advance_payment_entries(
+ self.party_type,
+ self.party,
+ self.receivable_payable_account,
+ order_doctype,
+ against_all_orders=True,
+ limit=self.payment_limit,
+ condition=condition,
+ )
return payment_entries
def get_jv_entries(self):
condition = self.get_conditions()
- dr_or_cr = ("credit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
- else "debit_in_account_currency")
-
- bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
- if self.bank_cash_account else "1=1"
-
- journal_entries = frappe.db.sql("""
+ dr_or_cr = (
+ "credit_in_account_currency"
+ if erpnext.get_party_account_type(self.party_type) == "Receivable"
+ else "debit_in_account_currency"
+ )
+
+ bank_account_condition = (
+ "t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
+ )
+
+ journal_entries = frappe.db.sql(
+ """
select
"Journal Entry" as reference_type, t1.name as reference_name,
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
@@ -76,31 +89,42 @@ def get_jv_entries(self):
ELSE {bank_account_condition}
END)
order by t1.posting_date
- """.format(**{
- "dr_or_cr": dr_or_cr,
- "bank_account_condition": bank_account_condition,
- "condition": condition
- }), {
+ """.format(
+ **{
+ "dr_or_cr": dr_or_cr,
+ "bank_account_condition": bank_account_condition,
+ "condition": condition,
+ }
+ ),
+ {
"party_type": self.party_type,
"party": self.party,
"account": self.receivable_payable_account,
- "bank_cash_account": "%%%s%%" % self.bank_cash_account
- }, as_dict=1)
+ "bank_cash_account": "%%%s%%" % self.bank_cash_account,
+ },
+ as_dict=1,
+ )
return list(journal_entries)
def get_dr_or_cr_notes(self):
condition = self.get_conditions(get_return_invoices=True)
- dr_or_cr = ("credit_in_account_currency"
- if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
-
- reconciled_dr_or_cr = ("debit_in_account_currency"
- if dr_or_cr == "credit_in_account_currency" else "credit_in_account_currency")
-
- voucher_type = ('Sales Invoice'
- if self.party_type == 'Customer' else "Purchase Invoice")
-
- return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
+ dr_or_cr = (
+ "credit_in_account_currency"
+ if erpnext.get_party_account_type(self.party_type) == "Receivable"
+ else "debit_in_account_currency"
+ )
+
+ reconciled_dr_or_cr = (
+ "debit_in_account_currency"
+ if dr_or_cr == "credit_in_account_currency"
+ else "credit_in_account_currency"
+ )
+
+ voucher_type = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
+
+ return frappe.db.sql(
+ """ SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount, doc.posting_date,
account_currency as currency
FROM `tab{doc}` doc, `tabGL Entry` gl
@@ -117,106 +141,115 @@ def get_dr_or_cr_notes(self):
amount > 0
ORDER BY doc.posting_date
""".format(
- doc=voucher_type,
- dr_or_cr=dr_or_cr,
- reconciled_dr_or_cr=reconciled_dr_or_cr,
- party_type_field=frappe.scrub(self.party_type),
- condition=condition or ""),
+ doc=voucher_type,
+ dr_or_cr=dr_or_cr,
+ reconciled_dr_or_cr=reconciled_dr_or_cr,
+ party_type_field=frappe.scrub(self.party_type),
+ condition=condition or "",
+ ),
{
- 'party': self.party,
- 'party_type': self.party_type,
- 'voucher_type': voucher_type,
- 'account': self.receivable_payable_account
- }, as_dict=1)
+ "party": self.party,
+ "party_type": self.party_type,
+ "voucher_type": voucher_type,
+ "account": self.receivable_payable_account,
+ },
+ as_dict=1,
+ )
def add_payment_entries(self, non_reconciled_payments):
- self.set('payments', [])
+ self.set("payments", [])
for payment in non_reconciled_payments:
- row = self.append('payments', {})
+ row = self.append("payments", {})
row.update(payment)
def get_invoice_entries(self):
- #Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
+ # Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
condition = self.get_conditions(get_invoices=True)
- non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party,
- self.receivable_payable_account, condition=condition)
+ non_reconciled_invoices = get_outstanding_invoices(
+ self.party_type, self.party, self.receivable_payable_account, condition=condition
+ )
if self.invoice_limit:
- non_reconciled_invoices = non_reconciled_invoices[:self.invoice_limit]
+ non_reconciled_invoices = non_reconciled_invoices[: self.invoice_limit]
self.add_invoice_entries(non_reconciled_invoices)
def add_invoice_entries(self, non_reconciled_invoices):
- #Populate 'invoices' with JVs and Invoices to reconcile against
- self.set('invoices', [])
+ # Populate 'invoices' with JVs and Invoices to reconcile against
+ self.set("invoices", [])
for entry in non_reconciled_invoices:
- inv = self.append('invoices', {})
- inv.invoice_type = entry.get('voucher_type')
- inv.invoice_number = entry.get('voucher_no')
- inv.invoice_date = entry.get('posting_date')
- inv.amount = flt(entry.get('invoice_amount'))
- inv.currency = entry.get('currency')
- inv.outstanding_amount = flt(entry.get('outstanding_amount'))
+ inv = self.append("invoices", {})
+ inv.invoice_type = entry.get("voucher_type")
+ inv.invoice_number = entry.get("voucher_no")
+ inv.invoice_date = entry.get("posting_date")
+ inv.amount = flt(entry.get("invoice_amount"))
+ inv.currency = entry.get("currency")
+ inv.outstanding_amount = flt(entry.get("outstanding_amount"))
@frappe.whitelist()
def allocate_entries(self, args):
self.validate_entries()
entries = []
- for pay in args.get('payments'):
- pay.update({'unreconciled_amount': pay.get('amount')})
- for inv in args.get('invoices'):
- if pay.get('amount') >= inv.get('outstanding_amount'):
- res = self.get_allocated_entry(pay, inv, inv['outstanding_amount'])
- pay['amount'] = flt(pay.get('amount')) - flt(inv.get('outstanding_amount'))
- inv['outstanding_amount'] = 0
+ for pay in args.get("payments"):
+ pay.update({"unreconciled_amount": pay.get("amount")})
+ for inv in args.get("invoices"):
+ if pay.get("amount") >= inv.get("outstanding_amount"):
+ res = self.get_allocated_entry(pay, inv, inv["outstanding_amount"])
+ pay["amount"] = flt(pay.get("amount")) - flt(inv.get("outstanding_amount"))
+ inv["outstanding_amount"] = 0
else:
- res = self.get_allocated_entry(pay, inv, pay['amount'])
- inv['outstanding_amount'] = flt(inv.get('outstanding_amount')) - flt(pay.get('amount'))
- pay['amount'] = 0
- if pay.get('amount') == 0:
+ res = self.get_allocated_entry(pay, inv, pay["amount"])
+ inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount"))
+ pay["amount"] = 0
+ if pay.get("amount") == 0:
entries.append(res)
break
- elif inv.get('outstanding_amount') == 0:
+ elif inv.get("outstanding_amount") == 0:
entries.append(res)
continue
else:
break
- self.set('allocation', [])
+ self.set("allocation", [])
for entry in entries:
- if entry['allocated_amount'] != 0:
- row = self.append('allocation', {})
+ if entry["allocated_amount"] != 0:
+ row = self.append("allocation", {})
row.update(entry)
def get_allocated_entry(self, pay, inv, allocated_amount):
- return frappe._dict({
- 'reference_type': pay.get('reference_type'),
- 'reference_name': pay.get('reference_name'),
- 'reference_row': pay.get('reference_row'),
- 'invoice_type': inv.get('invoice_type'),
- 'invoice_number': inv.get('invoice_number'),
- 'unreconciled_amount': pay.get('unreconciled_amount'),
- 'amount': pay.get('amount'),
- 'allocated_amount': allocated_amount,
- 'difference_amount': pay.get('difference_amount')
- })
+ return frappe._dict(
+ {
+ "reference_type": pay.get("reference_type"),
+ "reference_name": pay.get("reference_name"),
+ "reference_row": pay.get("reference_row"),
+ "invoice_type": inv.get("invoice_type"),
+ "invoice_number": inv.get("invoice_number"),
+ "unreconciled_amount": pay.get("unreconciled_amount"),
+ "amount": pay.get("amount"),
+ "allocated_amount": allocated_amount,
+ "difference_amount": pay.get("difference_amount"),
+ }
+ )
@frappe.whitelist()
def reconcile(self):
self.validate_allocation()
- dr_or_cr = ("credit_in_account_currency"
- if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
+ dr_or_cr = (
+ "credit_in_account_currency"
+ if erpnext.get_party_account_type(self.party_type) == "Receivable"
+ else "debit_in_account_currency"
+ )
entry_list = []
dr_or_cr_notes = []
- for row in self.get('allocation'):
+ for row in self.get("allocation"):
reconciled_entry = []
if row.invoice_number and row.allocated_amount:
- if row.reference_type in ['Sales Invoice', 'Purchase Invoice']:
+ if row.reference_type in ["Sales Invoice", "Purchase Invoice"]:
reconciled_entry = dr_or_cr_notes
else:
reconciled_entry = entry_list
@@ -233,23 +266,25 @@ def reconcile(self):
self.get_unreconciled_entries()
def get_payment_details(self, row, dr_or_cr):
- return frappe._dict({
- 'voucher_type': row.get('reference_type'),
- 'voucher_no' : row.get('reference_name'),
- 'voucher_detail_no' : row.get('reference_row'),
- 'against_voucher_type' : row.get('invoice_type'),
- 'against_voucher' : row.get('invoice_number'),
- 'account' : self.receivable_payable_account,
- 'party_type': self.party_type,
- 'party': self.party,
- 'is_advance' : row.get('is_advance'),
- 'dr_or_cr' : dr_or_cr,
- 'unreconciled_amount': flt(row.get('unreconciled_amount')),
- 'unadjusted_amount' : flt(row.get('amount')),
- 'allocated_amount' : flt(row.get('allocated_amount')),
- 'difference_amount': flt(row.get('difference_amount')),
- 'difference_account': row.get('difference_account')
- })
+ return frappe._dict(
+ {
+ "voucher_type": row.get("reference_type"),
+ "voucher_no": row.get("reference_name"),
+ "voucher_detail_no": row.get("reference_row"),
+ "against_voucher_type": row.get("invoice_type"),
+ "against_voucher": row.get("invoice_number"),
+ "account": self.receivable_payable_account,
+ "party_type": self.party_type,
+ "party": self.party,
+ "is_advance": row.get("is_advance"),
+ "dr_or_cr": dr_or_cr,
+ "unreconciled_amount": flt(row.get("unreconciled_amount")),
+ "unadjusted_amount": flt(row.get("amount")),
+ "allocated_amount": flt(row.get("allocated_amount")),
+ "difference_amount": flt(row.get("difference_amount")),
+ "difference_account": row.get("difference_account"),
+ }
+ )
def check_mandatory_to_fetch(self):
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
@@ -267,7 +302,9 @@ def validate_allocation(self):
unreconciled_invoices = frappe._dict()
for inv in self.get("invoices"):
- unreconciled_invoices.setdefault(inv.invoice_type, {}).setdefault(inv.invoice_number, inv.outstanding_amount)
+ unreconciled_invoices.setdefault(inv.invoice_type, {}).setdefault(
+ inv.invoice_number, inv.outstanding_amount
+ )
invoices_to_reconcile = []
for row in self.get("allocation"):
@@ -275,13 +312,19 @@ def validate_allocation(self):
invoices_to_reconcile.append(row.invoice_number)
if flt(row.amount) - flt(row.allocated_amount) < 0:
- frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2}")
- .format(row.idx, row.allocated_amount, row.amount))
+ frappe.throw(
+ _(
+ "Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2}"
+ ).format(row.idx, row.allocated_amount, row.amount)
+ )
invoice_outstanding = unreconciled_invoices.get(row.invoice_type, {}).get(row.invoice_number)
if flt(row.allocated_amount) - invoice_outstanding > 0.009:
- frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2}")
- .format(row.idx, row.allocated_amount, invoice_outstanding))
+ frappe.throw(
+ _(
+ "Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2}"
+ ).format(row.idx, row.allocated_amount, invoice_outstanding)
+ )
if not invoices_to_reconcile:
frappe.throw(_("No records found in Allocation table"))
@@ -290,10 +333,21 @@ def get_conditions(self, get_invoices=False, get_payments=False, get_return_invo
condition = " and company = '{0}' ".format(self.company)
if get_invoices:
- condition += " and posting_date >= {0}".format(frappe.db.escape(self.from_invoice_date)) if self.from_invoice_date else ""
- condition += " and posting_date <= {0}".format(frappe.db.escape(self.to_invoice_date)) if self.to_invoice_date else ""
- dr_or_cr = ("debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
- else "credit_in_account_currency")
+ condition += (
+ " and posting_date >= {0}".format(frappe.db.escape(self.from_invoice_date))
+ if self.from_invoice_date
+ else ""
+ )
+ condition += (
+ " and posting_date <= {0}".format(frappe.db.escape(self.to_invoice_date))
+ if self.to_invoice_date
+ else ""
+ )
+ dr_or_cr = (
+ "debit_in_account_currency"
+ if erpnext.get_party_account_type(self.party_type) == "Receivable"
+ else "credit_in_account_currency"
+ )
if self.minimum_invoice_amount:
condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_invoice_amount))
@@ -302,10 +356,21 @@ def get_conditions(self, get_invoices=False, get_payments=False, get_return_invo
elif get_return_invoices:
condition = " and doc.company = '{0}' ".format(self.company)
- condition += " and doc.posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) if self.from_payment_date else ""
- condition += " and doc.posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) if self.to_payment_date else ""
- dr_or_cr = ("gl.debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
- else "gl.credit_in_account_currency")
+ condition += (
+ " and doc.posting_date >= {0}".format(frappe.db.escape(self.from_payment_date))
+ if self.from_payment_date
+ else ""
+ )
+ condition += (
+ " and doc.posting_date <= {0}".format(frappe.db.escape(self.to_payment_date))
+ if self.to_payment_date
+ else ""
+ )
+ dr_or_cr = (
+ "gl.debit_in_account_currency"
+ if erpnext.get_party_account_type(self.party_type) == "Receivable"
+ else "gl.credit_in_account_currency"
+ )
if self.minimum_invoice_amount:
condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_payment_amount))
@@ -313,55 +378,77 @@ def get_conditions(self, get_invoices=False, get_payments=False, get_return_invo
condition += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_payment_amount))
else:
- condition += " and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) if self.from_payment_date else ""
- condition += " and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) if self.to_payment_date else ""
+ condition += (
+ " and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date))
+ if self.from_payment_date
+ else ""
+ )
+ condition += (
+ " and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date))
+ if self.to_payment_date
+ else ""
+ )
if self.minimum_payment_amount:
- condition += " and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount)) if get_payments \
+ condition += (
+ " and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount))
+ if get_payments
else " and total_debit >= {0}".format(flt(self.minimum_payment_amount))
+ )
if self.maximum_payment_amount:
- condition += " and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount)) if get_payments \
+ condition += (
+ " and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount))
+ if get_payments
else " and total_debit <= {0}".format(flt(self.maximum_payment_amount))
+ )
return condition
+
def reconcile_dr_cr_note(dr_cr_notes, company):
for inv in dr_cr_notes:
- voucher_type = ('Credit Note'
- if inv.voucher_type == 'Sales Invoice' else 'Debit Note')
+ voucher_type = "Credit Note" if inv.voucher_type == "Sales Invoice" else "Debit Note"
- reconcile_dr_or_cr = ('debit_in_account_currency'
- if inv.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
+ reconcile_dr_or_cr = (
+ "debit_in_account_currency"
+ if inv.dr_or_cr == "credit_in_account_currency"
+ else "credit_in_account_currency"
+ )
company_currency = erpnext.get_company_currency(company)
- jv = frappe.get_doc({
- "doctype": "Journal Entry",
- "voucher_type": voucher_type,
- "posting_date": today(),
- "company": company,
- "multi_currency": 1 if inv.currency != company_currency else 0,
- "accounts": [
- {
- 'account': inv.account,
- 'party': inv.party,
- 'party_type': inv.party_type,
- inv.dr_or_cr: abs(inv.allocated_amount),
- 'reference_type': inv.against_voucher_type,
- 'reference_name': inv.against_voucher,
- 'cost_center': erpnext.get_default_cost_center(company)
- },
- {
- 'account': inv.account,
- 'party': inv.party,
- 'party_type': inv.party_type,
- reconcile_dr_or_cr: (abs(inv.allocated_amount)
- if abs(inv.unadjusted_amount) > abs(inv.allocated_amount) else abs(inv.unadjusted_amount)),
- 'reference_type': inv.voucher_type,
- 'reference_name': inv.voucher_no,
- 'cost_center': erpnext.get_default_cost_center(company)
- }
- ]
- })
+ jv = frappe.get_doc(
+ {
+ "doctype": "Journal Entry",
+ "voucher_type": voucher_type,
+ "posting_date": today(),
+ "company": company,
+ "multi_currency": 1 if inv.currency != company_currency else 0,
+ "accounts": [
+ {
+ "account": inv.account,
+ "party": inv.party,
+ "party_type": inv.party_type,
+ inv.dr_or_cr: abs(inv.allocated_amount),
+ "reference_type": inv.against_voucher_type,
+ "reference_name": inv.against_voucher,
+ "cost_center": erpnext.get_default_cost_center(company),
+ },
+ {
+ "account": inv.account,
+ "party": inv.party,
+ "party_type": inv.party_type,
+ reconcile_dr_or_cr: (
+ abs(inv.allocated_amount)
+ if abs(inv.unadjusted_amount) > abs(inv.allocated_amount)
+ else abs(inv.unadjusted_amount)
+ ),
+ "reference_type": inv.voucher_type,
+ "reference_name": inv.voucher_no,
+ "cost_center": erpnext.get_default_cost_center(company),
+ },
+ ],
+ }
+ )
jv.flags.ignore_mandatory = True
jv.submit()
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index d72d8f701802..5264987acf77 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -24,7 +24,7 @@
class PaymentRequest(Document):
def validate(self):
if self.get("__islocal"):
- self.status = 'Draft'
+ self.status = "Draft"
self.validate_reference_document()
self.validate_payment_request_amount()
self.validate_currency()
@@ -35,51 +35,67 @@ def validate_reference_document(self):
frappe.throw(_("To create a Payment Request reference document is required"))
def validate_payment_request_amount(self):
- existing_payment_request_amount = \
- get_existing_payment_request_amount(self.reference_doctype, self.reference_name)
+ existing_payment_request_amount = get_existing_payment_request_amount(
+ self.reference_doctype, self.reference_name
+ )
if existing_payment_request_amount:
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 getattr(ref_doc, "order_type") != "Shopping Cart":
ref_amount = get_amount(ref_doc, self.payment_account)
- if existing_payment_request_amount + flt(self.grand_total)> ref_amount:
- frappe.throw(_("Total Payment Request amount cannot be greater than {0} amount")
- .format(self.reference_doctype))
+ if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
+ frappe.throw(
+ _("Total Payment Request amount cannot be greater than {0} amount").format(
+ self.reference_doctype
+ )
+ )
def validate_currency(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
- if self.payment_account and ref_doc.currency != frappe.db.get_value("Account", self.payment_account, "account_currency"):
+ if self.payment_account and ref_doc.currency != frappe.db.get_value(
+ "Account", self.payment_account, "account_currency"
+ ):
frappe.throw(_("Transaction currency must be same as Payment Gateway currency"))
def validate_subscription_details(self):
if self.is_a_subscription:
amount = 0
for subscription_plan in self.subscription_plans:
- payment_gateway = frappe.db.get_value("Subscription Plan", subscription_plan.plan, "payment_gateway")
+ payment_gateway = frappe.db.get_value(
+ "Subscription Plan", subscription_plan.plan, "payment_gateway"
+ )
if payment_gateway != self.payment_gateway_account:
- frappe.throw(_('The payment gateway account in plan {0} is different from the payment gateway account in this payment request').format(subscription_plan.name))
+ frappe.throw(
+ _(
+ "The payment gateway account in plan {0} is different from the payment gateway account in this payment request"
+ ).format(subscription_plan.name)
+ )
rate = get_plan_rate(subscription_plan.plan, quantity=subscription_plan.qty)
amount += rate
if amount != self.grand_total:
- frappe.msgprint(_("The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.").format(self.grand_total, amount))
+ frappe.msgprint(
+ _(
+ "The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document."
+ ).format(self.grand_total, amount)
+ )
def on_submit(self):
- if self.payment_request_type == 'Outward':
- self.db_set('status', 'Initiated')
+ if self.payment_request_type == "Outward":
+ self.db_set("status", "Initiated")
return
- elif self.payment_request_type == 'Inward':
- self.db_set('status', 'Requested')
+ elif self.payment_request_type == "Inward":
+ self.db_set("status", "Requested")
send_mail = self.payment_gateway_validation() if self.payment_gateway else None
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") \
- or self.flags.mute_email:
+ if (
+ hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"
+ ) or self.flags.mute_email:
send_mail = False
if send_mail and self.payment_channel != "Phone":
@@ -101,23 +117,27 @@ def request_phone_payment(self):
request_amount=request_amount,
sender=self.email_to,
currency=self.currency,
- payment_gateway=self.payment_gateway
+ payment_gateway=self.payment_gateway,
)
controller.validate_transaction_currency(self.currency)
controller.request_for_payment(**payment_record)
def get_request_amount(self):
- data_of_completed_requests = frappe.get_all("Integration Request", filters={
- 'reference_doctype': self.doctype,
- 'reference_docname': self.name,
- 'status': 'Completed'
- }, pluck="data")
+ data_of_completed_requests = frappe.get_all(
+ "Integration Request",
+ filters={
+ "reference_doctype": self.doctype,
+ "reference_docname": self.name,
+ "status": "Completed",
+ },
+ pluck="data",
+ )
if not data_of_completed_requests:
return self.grand_total
- request_amounts = sum(json.loads(d).get('request_amount') for d in data_of_completed_requests)
+ request_amounts = sum(json.loads(d).get("request_amount") for d in data_of_completed_requests)
return request_amounts
def on_cancel(self):
@@ -126,8 +146,9 @@ def on_cancel(self):
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 getattr(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)
si.allocate_advances_automatically = True
si = si.insert(ignore_permissions=True)
@@ -136,7 +157,7 @@ def make_invoice(self):
def payment_gateway_validation(self):
try:
controller = get_payment_gateway_controller(self.payment_gateway)
- if hasattr(controller, 'on_payment_request_submission'):
+ if hasattr(controller, "on_payment_request_submission"):
return controller.on_payment_request_submission(self)
else:
return True
@@ -148,36 +169,45 @@ def set_payment_request_url(self):
self.payment_url = self.get_payment_url()
if self.payment_url:
- self.db_set('payment_url', self.payment_url)
+ self.db_set("payment_url", self.payment_url)
- if self.payment_url or not self.payment_gateway_account \
- or (self.payment_gateway_account and self.payment_channel == "Phone"):
- self.db_set('status', 'Initiated')
+ if (
+ self.payment_url
+ or not self.payment_gateway_account
+ or (self.payment_gateway_account and self.payment_channel == "Phone")
+ ):
+ self.db_set("status", "Initiated")
def get_payment_url(self):
if self.reference_doctype != "Fees":
- data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["company", "customer_name"], as_dict=1)
+ data = frappe.db.get_value(
+ self.reference_doctype, self.reference_name, ["company", "customer_name"], as_dict=1
+ )
else:
- data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["student_name"], as_dict=1)
+ data = frappe.db.get_value(
+ self.reference_doctype, self.reference_name, ["student_name"], as_dict=1
+ )
data.update({"company": frappe.defaults.get_defaults().company})
controller = get_payment_gateway_controller(self.payment_gateway)
controller.validate_transaction_currency(self.currency)
- if hasattr(controller, 'validate_minimum_transaction_amount'):
+ if hasattr(controller, "validate_minimum_transaction_amount"):
controller.validate_minimum_transaction_amount(self.currency, self.grand_total)
- return controller.get_payment_url(**{
- "amount": flt(self.grand_total, self.precision("grand_total")),
- "title": data.company.encode("utf-8"),
- "description": self.subject.encode("utf-8"),
- "reference_doctype": "Payment Request",
- "reference_docname": self.name,
- "payer_email": self.email_to or frappe.session.user,
- "payer_name": frappe.safe_encode(data.customer_name),
- "order_id": self.name,
- "currency": self.currency
- })
+ return controller.get_payment_url(
+ **{
+ "amount": flt(self.grand_total, self.precision("grand_total")),
+ "title": data.company.encode("utf-8"),
+ "description": self.subject.encode("utf-8"),
+ "reference_doctype": "Payment Request",
+ "reference_docname": self.name,
+ "payer_email": self.email_to or frappe.session.user,
+ "payer_name": frappe.safe_encode(data.customer_name),
+ "order_id": self.name,
+ "currency": self.currency,
+ }
+ )
def set_as_paid(self):
if self.payment_channel == "Phone":
@@ -202,32 +232,47 @@ def create_payment_entry(self, submit=True):
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.base_grand_total
else:
party_amount = self.grand_total
- payment_entry = get_payment_entry(self.reference_doctype, self.reference_name, party_amount=party_amount,
- bank_account=self.payment_account, bank_amount=bank_amount)
+ payment_entry = get_payment_entry(
+ self.reference_doctype,
+ self.reference_name,
+ party_amount=party_amount,
+ bank_account=self.payment_account,
+ bank_amount=bank_amount,
+ )
- payment_entry.update({
- "reference_no": self.name,
- "reference_date": nowdate(),
- "remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(self.reference_doctype,
- self.reference_name, self.name)
- })
+ payment_entry.update(
+ {
+ "reference_no": self.name,
+ "reference_date": nowdate(),
+ "remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(
+ self.reference_doctype, self.reference_name, self.name
+ ),
+ }
+ )
if payment_entry.difference_amount:
company_details = get_company_defaults(ref_doc.company)
- payment_entry.append("deductions", {
- "account": company_details.exchange_gain_loss_account,
- "cost_center": company_details.cost_center,
- "amount": payment_entry.difference_amount
- })
+ payment_entry.append(
+ "deductions",
+ {
+ "account": company_details.exchange_gain_loss_account,
+ "cost_center": company_details.cost_center,
+ "amount": payment_entry.difference_amount,
+ },
+ )
if submit:
payment_entry.insert(ignore_permissions=True)
@@ -243,16 +288,23 @@ def send_email(self):
"subject": self.subject,
"message": self.get_message(),
"now": True,
- "attachments": [frappe.attach_print(self.reference_doctype, self.reference_name,
- file_name=self.reference_name, print_format=self.print_format)]}
- enqueue(method=frappe.sendmail, queue='short', timeout=300, is_async=True, **email_args)
+ "attachments": [
+ frappe.attach_print(
+ self.reference_doctype,
+ self.reference_name,
+ file_name=self.reference_name,
+ print_format=self.print_format,
+ )
+ ],
+ }
+ enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
def get_message(self):
"""return message with payment gateway link"""
context = {
"doc": frappe.get_doc(self.reference_doctype, self.reference_name),
- "payment_url": self.payment_url
+ "payment_url": self.payment_url,
}
if self.message:
@@ -266,22 +318,26 @@ def set_as_cancelled(self):
def check_if_payment_entry_exists(self):
if self.status == "Paid":
- if frappe.get_all("Payment Entry Reference",
+ if frappe.get_all(
+ "Payment Entry Reference",
filters={"reference_name": self.reference_name, "docstatus": ["<", 2]},
fields=["parent"],
- limit=1):
- frappe.throw(_("Payment Entry already exists"), title=_('Error'))
+ limit=1,
+ ):
+ frappe.throw(_("Payment Entry already exists"), title=_("Error"))
def make_communication_entry(self):
"""Make communication entry"""
- comm = frappe.get_doc({
- "doctype":"Communication",
- "subject": self.subject,
- "content": self.get_message(),
- "sent_or_received": "Sent",
- "reference_doctype": self.reference_doctype,
- "reference_name": self.reference_name
- })
+ comm = frappe.get_doc(
+ {
+ "doctype": "Communication",
+ "subject": self.subject,
+ "content": self.get_message(),
+ "sent_or_received": "Sent",
+ "reference_doctype": self.reference_doctype,
+ "reference_name": self.reference_name,
+ }
+ )
comm.insert(ignore_permissions=True)
def get_payment_success_url(self):
@@ -298,16 +354,17 @@ def on_payment_authorized(self, status=None):
self.set_as_paid()
# if shopping cart enabled and in session
- if (shopping_cart_settings.enabled and hasattr(frappe.local, "session")
- and frappe.local.session.user != "Guest") and self.payment_channel != "Phone":
+ if (
+ shopping_cart_settings.enabled
+ and hasattr(frappe.local, "session")
+ and frappe.local.session.user != "Guest"
+ ) and self.payment_channel != "Phone":
success_url = shopping_cart_settings.payment_success_url
if success_url:
- redirect_to = ({
- "Orders": "/orders",
- "Invoices": "/invoices",
- "My Account": "/me"
- }).get(success_url, "/me")
+ redirect_to = ({"Orders": "/orders", "Invoices": "/invoices", "My Account": "/me"}).get(
+ success_url, "/me"
+ )
else:
redirect_to = get_url("/orders/{0}".format(self.reference_name))
@@ -317,6 +374,7 @@ def create_subscription(self, payment_provider, gateway_controller, data):
if payment_provider == "stripe":
return create_stripe_subscription(gateway_controller, data)
+
@frappe.whitelist(allow_guest=True)
def make_payment_request(**args):
"""Make payment request"""
@@ -329,49 +387,62 @@ def make_payment_request(**args):
grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
if args.loyalty_points and args.dt == "Sales Order":
from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
+
loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points))
- 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_points", int(args.loyalty_points), 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 '')
+ bank_account = (
+ get_party_bank_account(args.get("party_type"), args.get("party"))
+ if args.get("party_type")
+ else ""
+ )
existing_payment_request = None
if args.order_type == "Shopping Cart":
- existing_payment_request = frappe.db.get_value("Payment Request",
- {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)})
+ existing_payment_request = frappe.db.get_value(
+ "Payment Request",
+ {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)},
+ )
if existing_payment_request:
- frappe.db.set_value("Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False)
+ frappe.db.set_value(
+ "Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False
+ )
pr = frappe.get_doc("Payment Request", existing_payment_request)
else:
if args.order_type != "Shopping Cart":
- existing_payment_request_amount = \
- get_existing_payment_request_amount(args.dt, args.dn)
+ existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
if existing_payment_request_amount:
grand_total -= existing_payment_request_amount
pr = frappe.new_doc("Payment Request")
- pr.update({
- "payment_gateway_account": gateway_account.get("name"),
- "payment_gateway": gateway_account.get("payment_gateway"),
- "payment_account": gateway_account.get("payment_account"),
- "payment_channel": gateway_account.get("payment_channel"),
- "payment_request_type": args.get("payment_request_type"),
- "currency": ref_doc.currency,
- "grand_total": grand_total,
- "mode_of_payment": args.mode_of_payment,
- "email_to": args.recipient_id or ref_doc.owner,
- "subject": _("Payment Request for {0}").format(args.dn),
- "message": gateway_account.get("message") or get_dummy_message(ref_doc),
- "reference_doctype": args.dt,
- "reference_name": args.dn,
- "party_type": args.get("party_type") or "Customer",
- "party": args.get("party") or ref_doc.get("customer"),
- "bank_account": bank_account
- })
+ pr.update(
+ {
+ "payment_gateway_account": gateway_account.get("name"),
+ "payment_gateway": gateway_account.get("payment_gateway"),
+ "payment_account": gateway_account.get("payment_account"),
+ "payment_channel": gateway_account.get("payment_channel"),
+ "payment_request_type": args.get("payment_request_type"),
+ "currency": ref_doc.currency,
+ "grand_total": grand_total,
+ "mode_of_payment": args.mode_of_payment,
+ "email_to": args.recipient_id or ref_doc.owner,
+ "subject": _("Payment Request for {0}").format(args.dn),
+ "message": gateway_account.get("message") or get_dummy_message(ref_doc),
+ "reference_doctype": args.dt,
+ "reference_name": args.dn,
+ "party_type": args.get("party_type") or "Customer",
+ "party": args.get("party") or ref_doc.get("customer"),
+ "bank_account": bank_account,
+ }
+ )
if args.order_type == "Shopping Cart" or args.mute_email:
pr.flags.mute_email = True
@@ -390,6 +461,7 @@ def make_payment_request(**args):
return pr.as_dict()
+
def get_amount(ref_doc, payment_account=None):
"""get amount based on doctype"""
dt = ref_doc.doctype
@@ -411,18 +483,20 @@ def get_amount(ref_doc, payment_account=None):
elif dt == "Fees":
grand_total = ref_doc.outstanding_amount
- if grand_total > 0 :
+ if grand_total > 0:
return grand_total
else:
frappe.throw(_("Payment Entry is already created"))
+
def get_existing_payment_request_amount(ref_dt, ref_dn):
"""
Get the existing payment request which are unpaid or partially paid for payment channel other than Phone
and get the summation of existing paid payment request for Phone payment channel.
"""
- existing_payment_request_amount = frappe.db.sql("""
+ existing_payment_request_amount = frappe.db.sql(
+ """
select sum(grand_total)
from `tabPayment Request`
where
@@ -432,10 +506,13 @@ def get_existing_payment_request_amount(ref_dt, ref_dn):
and (status != 'Paid'
or (payment_channel = 'Phone'
and status = 'Paid'))
- """, (ref_dt, ref_dn))
+ """,
+ (ref_dt, ref_dn),
+ )
return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0
-def get_gateway_details(args): # nosemgrep
+
+def get_gateway_details(args): # nosemgrep
"""return gateway and payment account of default payment gateway"""
if args.get("payment_gateway_account"):
return get_payment_gateway_account(args.get("payment_gateway_account"))
@@ -448,58 +525,74 @@ def get_gateway_details(args): # nosemgrep
return gateway_account
+
def get_payment_gateway_account(args):
- return frappe.db.get_value("Payment Gateway Account", args,
+ return frappe.db.get_value(
+ "Payment Gateway Account",
+ args,
["name", "payment_gateway", "payment_account", "message"],
- as_dict=1)
+ as_dict=1,
+ )
+
@frappe.whitelist()
def get_print_format_list(ref_doctype):
print_format_list = ["Standard"]
- print_format_list.extend([p.name for p in frappe.get_all("Print Format",
- filters={"doc_type": ref_doctype})])
+ print_format_list.extend(
+ [p.name for p in frappe.get_all("Print Format", filters={"doc_type": ref_doctype})]
+ )
+
+ return {"print_format": print_format_list}
- return {
- "print_format": print_format_list
- }
@frappe.whitelist(allow_guest=True)
def resend_payment_email(docname):
return frappe.get_doc("Payment Request", docname).send_email()
+
@frappe.whitelist()
def make_payment_entry(docname):
doc = frappe.get_doc("Payment Request", docname)
return doc.create_payment_entry(submit=False).as_dict()
+
def update_payment_req_status(doc, method):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
for ref in doc.references:
- payment_request_name = frappe.db.get_value("Payment Request",
- {"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
- "docstatus": 1})
+ payment_request_name = frappe.db.get_value(
+ "Payment Request",
+ {
+ "reference_doctype": ref.reference_doctype,
+ "reference_name": ref.reference_name,
+ "docstatus": 1,
+ },
+ )
if payment_request_name:
- ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency)
- pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
+ ref_details = get_reference_details(
+ ref.reference_doctype, ref.reference_name, doc.party_account_currency
+ )
+ pay_req_doc = frappe.get_doc("Payment Request", payment_request_name)
status = pay_req_doc.status
if status != "Paid" and not ref_details.outstanding_amount:
- status = 'Paid'
+ status = "Paid"
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
- status = 'Partially Paid'
+ status = "Partially Paid"
elif ref_details.outstanding_amount == ref_details.total_amount:
- if pay_req_doc.payment_request_type == 'Outward':
- status = 'Initiated'
- elif pay_req_doc.payment_request_type == 'Inward':
- status = 'Requested'
+ if pay_req_doc.payment_request_type == "Outward":
+ status = "Initiated"
+ elif pay_req_doc.payment_request_type == "Inward":
+ status = "Requested"
+
+ pay_req_doc.db_set("status", status)
- pay_req_doc.db_set('status', status)
def get_dummy_message(doc):
- return frappe.render_template("""{% if doc.contact_person -%}
+ return frappe.render_template(
+ """{% if doc.contact_person -%}
Dear {{ doc.contact_person }},
{%- else %}Hello,
{% endif %} @@ -511,12 +604,19 @@ def get_dummy_message(doc):{{ _("If you have any questions, please get back to us.") }}
{{ _("Thank you for your business!") }}
-""", dict(doc=doc, payment_url = '{{ payment_url }}')) +""", + dict(doc=doc, payment_url="{{ payment_url }}"), + ) + @frappe.whitelist() def get_subscription_details(reference_doctype, reference_name): if reference_doctype == "Sales Invoice": - subscriptions = frappe.db.sql("""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",reference_name, as_dict=1) + subscriptions = frappe.db.sql( + """SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""", + reference_name, + as_dict=1, + ) subscription_plans = [] for subscription in subscriptions: plans = frappe.get_doc("Subscription", subscription.sub_name).plans @@ -524,38 +624,50 @@ def get_subscription_details(reference_doctype, reference_name): subscription_plans.append(plan) return subscription_plans + @frappe.whitelist() def make_payment_order(source_name, target_doc=None): from frappe.model.mapper import get_mapped_doc + def set_missing_values(source, target): target.payment_order_type = "Payment Request" - target.append('references', { - 'reference_doctype': source.reference_doctype, - 'reference_name': source.reference_name, - 'amount': source.grand_total, - 'supplier': source.party, - 'payment_request': source_name, - 'mode_of_payment': source.mode_of_payment, - 'bank_account': source.bank_account, - 'account': source.account - }) - - doclist = get_mapped_doc("Payment Request", source_name, { - "Payment Request": { - "doctype": "Payment Order", - } - }, target_doc, set_missing_values) + target.append( + "references", + { + "reference_doctype": source.reference_doctype, + "reference_name": source.reference_name, + "amount": source.grand_total, + "supplier": source.party, + "payment_request": source_name, + "mode_of_payment": source.mode_of_payment, + "bank_account": source.bank_account, + "account": source.account, + }, + ) + + doclist = get_mapped_doc( + "Payment Request", + source_name, + { + "Payment Request": { + "doctype": "Payment Order", + } + }, + target_doc, + set_missing_values, + ) return doclist + def validate_payment(doc, method=None): if doc.reference_doctype != "Payment Request" or ( - frappe.db.get_value(doc.reference_doctype, doc.reference_docname, 'status') - != "Paid" + frappe.db.get_value(doc.reference_doctype, doc.reference_docname, "status") != "Paid" ): return frappe.throw( - _("The Payment Request {0} is already paid, cannot process payment twice") - .format(doc.reference_docname) + _("The Payment Request {0} is already paid, cannot process payment twice").format( + doc.reference_docname + ) ) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index f679ccfe4ff0..477c726940cd 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -12,10 +12,7 @@ test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"] -payment_gateway = { - "doctype": "Payment Gateway", - "gateway": "_Test Gateway" -} +payment_gateway = {"doctype": "Payment Gateway", "gateway": "_Test Gateway"} payment_method = [ { @@ -23,30 +20,38 @@ "is_default": 1, "payment_gateway": "_Test Gateway", "payment_account": "_Test Bank - _TC", - "currency": "INR" + "currency": "INR", }, { "doctype": "Payment Gateway Account", "payment_gateway": "_Test Gateway", "payment_account": "_Test Bank USD - _TC", - "currency": "USD" - } + "currency": "USD", + }, ] + class TestPaymentRequest(unittest.TestCase): def setUp(self): if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"): frappe.get_doc(payment_gateway).insert(ignore_permissions=True) for method in payment_method: - if not frappe.db.get_value("Payment Gateway Account", {"payment_gateway": method["payment_gateway"], - "currency": method["currency"]}, "name"): + if not frappe.db.get_value( + "Payment Gateway Account", + {"payment_gateway": method["payment_gateway"], "currency": method["currency"]}, + "name", + ): frappe.get_doc(method).insert(ignore_permissions=True) def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR") - pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com", - payment_gateway_account="_Test Gateway - INR") + pr = make_payment_request( + dt="Sales Order", + dn=so_inr.name, + recipient_id="saurabh@erpnext.com", + payment_gateway_account="_Test Gateway - INR", + ) self.assertEqual(pr.reference_doctype, "Sales Order") self.assertEqual(pr.reference_name, so_inr.name) @@ -55,45 +60,75 @@ def test_payment_request_linkings(self): conversion_rate = get_exchange_rate("USD", "INR") si_usd = create_sales_invoice(currency="USD", conversion_rate=conversion_rate) - pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", - payment_gateway_account="_Test Gateway - USD") + pr = make_payment_request( + dt="Sales Invoice", + dn=si_usd.name, + recipient_id="saurabh@erpnext.com", + payment_gateway_account="_Test Gateway - USD", + ) self.assertEqual(pr.reference_doctype, "Sales Invoice") self.assertEqual(pr.reference_name, si_usd.name) self.assertEqual(pr.currency, "USD") def test_payment_entry(self): - frappe.db.set_value("Company", "_Test Company", - "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC") + frappe.db.set_value( + "Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC" + ) frappe.db.set_value("Company", "_Test Company", "write_off_account", "_Test Write Off - _TC") frappe.db.set_value("Company", "_Test Company", "cost_center", "_Test Cost Center - _TC") so_inr = make_sales_order(currency="INR") - pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com", - mute_email=1, payment_gateway_account="_Test Gateway - INR", submit_doc=1, return_doc=1) + pr = make_payment_request( + dt="Sales Order", + dn=so_inr.name, + recipient_id="saurabh@erpnext.com", + mute_email=1, + payment_gateway_account="_Test Gateway - INR", + submit_doc=1, + return_doc=1, + ) pe = pr.set_as_paid() so_inr = frappe.get_doc("Sales Order", so_inr.name) self.assertEqual(so_inr.advance_paid, 1000) - si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", - currency="USD", conversion_rate=50) - - pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", - mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1) + si_usd = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=50, + ) + + pr = make_payment_request( + dt="Sales Invoice", + dn=si_usd.name, + recipient_id="saurabh@erpnext.com", + mute_email=1, + payment_gateway_account="_Test Gateway - USD", + submit_doc=1, + return_doc=1, + ) pe = pr.set_as_paid() - expected_gle = dict((d[0], d) for d in [ - ["_Test Receivable USD - _TC", 0, 5000, si_usd.name], - [pr.payment_account, 6290.0, 0, None], - ["_Test Exchange Gain/Loss - _TC", 0, 1290, None] - ]) - - gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher + expected_gle = dict( + (d[0], d) + for d in [ + ["_Test Receivable USD - _TC", 0, 5000, si_usd.name], + [pr.payment_account, 6290.0, 0, None], + ["_Test Exchange Gain/Loss - _TC", 0, 1290, None], + ] + ) + + gl_entries = frappe.db.sql( + """select account, debit, credit, against_voucher from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s - order by account asc""", pe.name, as_dict=1) + order by account asc""", + pe.name, + as_dict=1, + ) self.assertTrue(gl_entries) @@ -104,35 +139,48 @@ def test_payment_entry(self): self.assertEqual(expected_gle[gle.account][3], gle.against_voucher) def test_status(self): - si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", - currency="USD", conversion_rate=50) - - pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", - mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1) + si_usd = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=50, + ) + + pr = make_payment_request( + dt="Sales Invoice", + dn=si_usd.name, + recipient_id="saurabh@erpnext.com", + mute_email=1, + payment_gateway_account="_Test Gateway - USD", + submit_doc=1, + return_doc=1, + ) pe = pr.create_payment_entry() pr.load_from_db() - self.assertEqual(pr.status, 'Paid') + self.assertEqual(pr.status, "Paid") pe.cancel() pr.load_from_db() - self.assertEqual(pr.status, 'Requested') + self.assertEqual(pr.status, "Requested") def test_multiple_payment_entries_against_sales_order(self): # Make Sales Order, grand_total = 1000 so = make_sales_order() # Payment Request amount = 200 - pr1 = make_payment_request(dt="Sales Order", dn=so.name, - recipient_id="nabin@erpnext.com", return_doc=1) + pr1 = make_payment_request( + dt="Sales Order", dn=so.name, recipient_id="nabin@erpnext.com", return_doc=1 + ) pr1.grand_total = 200 pr1.submit() # Make a 2nd Payment Request - pr2 = make_payment_request(dt="Sales Order", dn=so.name, - recipient_id="nabin@erpnext.com", return_doc=1) + pr2 = make_payment_request( + dt="Sales Order", dn=so.name, recipient_id="nabin@erpnext.com", return_doc=1 + ) self.assertEqual(pr2.grand_total, 800) diff --git a/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py b/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py index ac80b794bab9..8df97bf6c773 100644 --- a/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py +++ b/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py @@ -3,18 +3,10 @@ def get_data(): return { - 'fieldname': 'payment_term', - 'transactions': [ - { - 'label': _('Sales'), - 'items': ['Sales Invoice', 'Sales Order', 'Quotation'] - }, - { - 'label': _('Purchase'), - 'items': ['Purchase Invoice', 'Purchase Order'] - }, - { - 'items': ['Payment Terms Template'] - } - ] + "fieldname": "payment_term", + "transactions": [ + {"label": _("Sales"), "items": ["Sales Invoice", "Sales Order", "Quotation"]}, + {"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order"]}, + {"items": ["Payment Terms Template"]}, + ], } diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py index 3a6999c57999..ea3b76c52438 100644 --- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py +++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py @@ -16,10 +16,12 @@ def validate(self): def validate_invoice_portion(self): total_portion = 0 for term in self.terms: - total_portion += flt(term.get('invoice_portion', 0)) + 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 check_duplicate_terms(self): terms = [] @@ -27,8 +29,9 @@ def check_duplicate_terms(self): term_info = (term.payment_term, term.credit_days, term.credit_months, term.due_date_based_on) if term_info in terms: frappe.msgprint( - _('The Payment Term at row {0} is possibly a duplicate.').format(term.idx), - raise_exception=1, indicator='red' + _("The Payment Term at row {0} is possibly a duplicate.").format(term.idx), + raise_exception=1, + indicator="red", ) else: terms.append(term_info) diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py index 2cf7a6cf7a2f..34ac773febcc 100644 --- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py +++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py @@ -3,29 +3,17 @@ def get_data(): return { - 'fieldname': 'payment_terms_template', - 'non_standard_fieldnames': { - 'Customer Group': 'payment_terms', - 'Supplier Group': 'payment_terms', - 'Supplier': 'payment_terms', - 'Customer': 'payment_terms' + "fieldname": "payment_terms_template", + "non_standard_fieldnames": { + "Customer Group": "payment_terms", + "Supplier Group": "payment_terms", + "Supplier": "payment_terms", + "Customer": "payment_terms", }, - 'transactions': [ - { - 'label': _('Sales'), - 'items': ['Sales Invoice', 'Sales Order', 'Quotation'] - }, - { - 'label': _('Purchase'), - 'items': ['Purchase Invoice', 'Purchase Order'] - }, - { - 'label': _('Party'), - 'items': ['Customer', 'Supplier'] - }, - { - 'label': _('Group'), - 'items': ['Customer Group', 'Supplier Group'] - } - ] + "transactions": [ + {"label": _("Sales"), "items": ["Sales Invoice", "Sales Order", "Quotation"]}, + {"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order"]}, + {"label": _("Party"), "items": ["Customer", "Supplier"]}, + {"label": _("Group"), "items": ["Customer Group", "Supplier Group"]}, + ], } diff --git a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py index 8529ef5c3d5e..9717f2009a0b 100644 --- a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py +++ b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py @@ -8,64 +8,76 @@ class TestPaymentTermsTemplate(unittest.TestCase): def tearDown(self): - frappe.delete_doc('Payment Terms Template', '_Test Payment Terms Template For Test', force=1) + frappe.delete_doc("Payment Terms Template", "_Test Payment Terms Template For Test", force=1) def test_create_template(self): - template = frappe.get_doc({ - 'doctype': 'Payment Terms Template', - 'template_name': '_Test Payment Terms Template For Test', - 'terms': [{ - 'doctype': 'Payment Terms Template Detail', - 'invoice_portion': 50.00, - 'credit_days_based_on': 'Day(s) after invoice date', - 'credit_days': 30 - }] - }) + template = frappe.get_doc( + { + "doctype": "Payment Terms Template", + "template_name": "_Test Payment Terms Template For Test", + "terms": [ + { + "doctype": "Payment Terms Template Detail", + "invoice_portion": 50.00, + "credit_days_based_on": "Day(s) after invoice date", + "credit_days": 30, + } + ], + } + ) self.assertRaises(frappe.ValidationError, template.insert) - template.append('terms', { - 'doctype': 'Payment Terms Template Detail', - 'invoice_portion': 50.00, - 'credit_days_based_on': 'Day(s) after invoice date', - 'credit_days': 0 - }) + template.append( + "terms", + { + "doctype": "Payment Terms Template Detail", + "invoice_portion": 50.00, + "credit_days_based_on": "Day(s) after invoice date", + "credit_days": 0, + }, + ) template.insert() def test_credit_days(self): - template = frappe.get_doc({ - 'doctype': 'Payment Terms Template', - 'template_name': '_Test Payment Terms Template For Test', - 'terms': [{ - 'doctype': 'Payment Terms Template Detail', - 'invoice_portion': 100.00, - 'credit_days_based_on': 'Day(s) after invoice date', - 'credit_days': -30 - }] - }) + template = frappe.get_doc( + { + "doctype": "Payment Terms Template", + "template_name": "_Test Payment Terms Template For Test", + "terms": [ + { + "doctype": "Payment Terms Template Detail", + "invoice_portion": 100.00, + "credit_days_based_on": "Day(s) after invoice date", + "credit_days": -30, + } + ], + } + ) self.assertRaises(frappe.ValidationError, template.insert) def test_duplicate_terms(self): - template = frappe.get_doc({ - 'doctype': 'Payment Terms Template', - 'template_name': '_Test Payment Terms Template For Test', - 'terms': [ - { - 'doctype': 'Payment Terms Template Detail', - 'invoice_portion': 50.00, - 'credit_days_based_on': 'Day(s) after invoice date', - 'credit_days': 30 - }, - { - 'doctype': 'Payment Terms Template Detail', - 'invoice_portion': 50.00, - 'credit_days_based_on': 'Day(s) after invoice date', - 'credit_days': 30 - } - - ] - }) + template = frappe.get_doc( + { + "doctype": "Payment Terms Template", + "template_name": "_Test Payment Terms Template For Test", + "terms": [ + { + "doctype": "Payment Terms Template Detail", + "invoice_portion": 50.00, + "credit_days_based_on": "Day(s) after invoice date", + "credit_days": 30, + }, + { + "doctype": "Payment Terms Template Detail", + "invoice_portion": 50.00, + "credit_days_based_on": "Day(s) after invoice date", + "credit_days": 30, + }, + ], + } + ) self.assertRaises(frappe.ValidationError, template.insert) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index d0e555e40c72..53b1c64c4603 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -23,40 +23,52 @@ def on_submit(self): self.make_gl_entries() def on_cancel(self): - self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") from erpnext.accounts.general_ledger import make_reverse_gl_entries + make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name) def validate_account_head(self): closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type") if closing_account_type not in ["Liability", "Equity"]: - frappe.throw(_("Closing Account {0} must be of type Liability / Equity") - .format(self.closing_account_head)) + frappe.throw( + _("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head) + ) account_currency = get_account_currency(self.closing_account_head) - company_currency = frappe.get_cached_value('Company', self.company, "default_currency") + company_currency = frappe.get_cached_value("Company", self.company, "default_currency") if account_currency != company_currency: frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency)) def validate_posting_date(self): from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year - validate_fiscal_year(self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self) + validate_fiscal_year( + 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] - pce = frappe.db.sql("""select name from `tabPeriod Closing Voucher` + pce = frappe.db.sql( + """select name from `tabPeriod Closing Voucher` where posting_date > %s and fiscal_year = %s and docstatus = 1""", - (self.posting_date, self.fiscal_year)) + (self.posting_date, self.fiscal_year), + ) if pce and pce[0][0]: - frappe.throw(_("Another Period Closing Entry {0} has been made after {1}") - .format(pce[0][0], self.posting_date)) + frappe.throw( + _("Another Period Closing Entry {0} has been made after {1}").format( + pce[0][0], self.posting_date + ) + ) def make_gl_entries(self): gl_entries = self.get_gl_entries() if gl_entries: from erpnext.accounts.general_ledger import make_gl_entries + make_gl_entries(gl_entries) def get_gl_entries(self): @@ -65,16 +77,29 @@ def get_gl_entries(self): for acc in pl_accounts: if flt(acc.bal_in_company_currency): - gl_entries.append(self.get_gl_dict({ - "account": acc.account, - "cost_center": acc.cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0, - "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, - "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 - }, item=acc)) + gl_entries.append( + self.get_gl_dict( + { + "account": acc.account, + "cost_center": acc.cost_center, + "finance_book": acc.finance_book, + "account_currency": acc.account_currency, + "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) + if flt(acc.bal_in_account_currency) < 0 + else 0, + "debit": abs(flt(acc.bal_in_company_currency)) + if flt(acc.bal_in_company_currency) < 0 + else 0, + "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, + }, + item=acc, + ) + ) if gl_entries: gle_for_net_pl_bal = self.get_pnl_gl_entry(pl_accounts) @@ -89,16 +114,27 @@ def get_pnl_gl_entry(self, pl_accounts): for acc in pl_accounts: if flt(acc.bal_in_company_currency): cost_center = acc.cost_center if self.cost_center_wise_pnl else company_cost_center - gl_entry = self.get_gl_dict({ - "account": self.closing_account_head, - "cost_center": cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0, - "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0, - "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 - }, item=acc) + gl_entry = self.get_gl_dict( + { + "account": self.closing_account_head, + "cost_center": cost_center, + "finance_book": acc.finance_book, + "account_currency": acc.account_currency, + "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) + if flt(acc.bal_in_account_currency) > 0 + else 0, + "debit": abs(flt(acc.bal_in_company_currency)) + if flt(acc.bal_in_company_currency) > 0 + else 0, + "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, + }, + item=acc, + ) self.update_default_dimensions(gl_entry) @@ -112,20 +148,19 @@ def update_default_dimensions(self, gl_entry): _, default_dimensions = get_dimensions() for dimension in self.accounting_dimensions: - gl_entry.update({ - dimension: default_dimensions.get(self.company, {}).get(dimension) - }) + gl_entry.update({dimension: default_dimensions.get(self.company, {}).get(dimension)}) def get_pl_balances(self): """Get balance for dimension-wise pl accounts""" - dimension_fields = ['t1.cost_center', 't1.finance_book'] + dimension_fields = ["t1.cost_center", "t1.finance_book"] self.accounting_dimensions = get_accounting_dimensions() for dimension in self.accounting_dimensions: - dimension_fields.append('t1.{0}'.format(dimension)) + dimension_fields.append("t1.{0}".format(dimension)) - return frappe.db.sql(""" + return frappe.db.sql( + """ select t1.account, t2.account_currency, {dimension_fields}, sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency, @@ -135,4 +170,9 @@ def get_pl_balances(self): and t2.docstatus < 2 and t2.company = %s and t1.posting_date between %s and %s group by t1.account, {dimension_fields} - """.format(dimension_fields = ', '.join(dimension_fields)), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1) + """.format( + dimension_fields=", ".join(dimension_fields) + ), + (self.company, self.get("year_start_date"), self.posting_date), + as_dict=1, + ) diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index 030b4caf7ca8..8e0e62d5f8c2 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -2,7 +2,6 @@ # License: GNU General Public License v3. See license.txt - import unittest import frappe @@ -19,7 +18,7 @@ def test_closing_entry(self): frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") company = create_company() - cost_center = create_cost_center('Test Cost Center 1') + cost_center = create_cost_center("Test Cost Center 1") jv1 = make_journal_entry( amount=400, @@ -27,7 +26,7 @@ def test_closing_entry(self): account2="Sales - TPC", cost_center=cost_center, posting_date=now(), - save=False + save=False, ) jv1.company = company jv1.save() @@ -39,7 +38,7 @@ def test_closing_entry(self): account2="Cash - TPC", cost_center=cost_center, posting_date=now(), - save=False + save=False, ) jv2.company = company jv2.save() @@ -49,14 +48,17 @@ def test_closing_entry(self): surplus_account = pcv.closing_account_head expected_gle = ( - ('Cost of Goods Sold - TPC', 0.0, 600.0), + ("Cost of Goods Sold - TPC", 0.0, 600.0), (surplus_account, 600.0, 400.0), - ('Sales - TPC', 400.0, 0.0) + ("Sales - TPC", 400.0, 0.0), ) - pcv_gle = frappe.db.sql(""" + pcv_gle = frappe.db.sql( + """ select account, debit, credit from `tabGL Entry` where voucher_no=%s order by account - """, (pcv.name)) + """, + (pcv.name), + ) self.assertEqual(pcv_gle, expected_gle) @@ -75,7 +77,7 @@ def test_cost_center_wise_posting(self): income_account="Sales - TPC", expense_account="Cost of Goods Sold - TPC", rate=400, - debit_to="Debtors - TPC" + debit_to="Debtors - TPC", ) create_sales_invoice( company=company, @@ -83,7 +85,7 @@ def test_cost_center_wise_posting(self): income_account="Sales - TPC", expense_account="Cost of Goods Sold - TPC", rate=200, - debit_to="Debtors - TPC" + debit_to="Debtors - TPC", ) pcv = self.make_period_closing_voucher(submit=False) @@ -95,15 +97,18 @@ def test_cost_center_wise_posting(self): expected_gle = ( (surplus_account, 0.0, 400.0, cost_center1), (surplus_account, 0.0, 200.0, cost_center2), - ('Sales - TPC', 400.0, 0.0, cost_center1), - ('Sales - TPC', 200.0, 0.0, cost_center2), + ("Sales - TPC", 400.0, 0.0, cost_center1), + ("Sales - TPC", 200.0, 0.0, cost_center2), ) - pcv_gle = frappe.db.sql(""" + pcv_gle = frappe.db.sql( + """ select account, debit, credit, cost_center from `tabGL Entry` where voucher_no=%s order by account, cost_center - """, (pcv.name)) + """, + (pcv.name), + ) self.assertEqual(pcv_gle, expected_gle) @@ -120,14 +125,14 @@ def test_period_closing_with_finance_book_entries(self): expense_account="Cost of Goods Sold - TPC", cost_center=cost_center, rate=400, - debit_to="Debtors - TPC" + debit_to="Debtors - TPC", ) jv = make_journal_entry( account1="Cash - TPC", account2="Sales - TPC", amount=400, cost_center=cost_center, - posting_date=now() + posting_date=now(), ) jv.company = company jv.finance_book = create_finance_book().name @@ -140,69 +145,84 @@ def test_period_closing_with_finance_book_entries(self): expected_gle = ( (surplus_account, 0.0, 400.0, None), (surplus_account, 0.0, 400.0, jv.finance_book), - ('Sales - TPC', 400.0, 0.0, None), - ('Sales - TPC', 400.0, 0.0, jv.finance_book) + ("Sales - TPC", 400.0, 0.0, None), + ("Sales - TPC", 400.0, 0.0, jv.finance_book), ) - pcv_gle = frappe.db.sql(""" + pcv_gle = frappe.db.sql( + """ select account, debit, credit, finance_book from `tabGL Entry` where voucher_no=%s order by account, finance_book - """, (pcv.name)) + """, + (pcv.name), + ) self.assertEqual(pcv_gle, expected_gle) def make_period_closing_voucher(self, submit=True): surplus_account = create_account() cost_center = create_cost_center("Test Cost Center 1") - pcv = frappe.get_doc({ - "doctype": "Period Closing Voucher", - "transaction_date": today(), - "posting_date": today(), - "company": "Test PCV Company", - "fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0], - "cost_center": cost_center, - "closing_account_head": surplus_account, - "remarks": "test" - }) + pcv = frappe.get_doc( + { + "doctype": "Period Closing Voucher", + "transaction_date": today(), + "posting_date": today(), + "company": "Test PCV Company", + "fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0], + "cost_center": cost_center, + "closing_account_head": surplus_account, + "remarks": "test", + } + ) pcv.insert() if submit: pcv.submit() return pcv + def create_company(): - company = frappe.get_doc({ - 'doctype': 'Company', - 'company_name': "Test PCV Company", - 'country': 'United States', - 'default_currency': 'USD' - }) - company.insert(ignore_if_duplicate = True) + company = frappe.get_doc( + { + "doctype": "Company", + "company_name": "Test PCV Company", + "country": "United States", + "default_currency": "USD", + } + ) + company.insert(ignore_if_duplicate=True) return company.name + def create_account(): - account = frappe.get_doc({ - "account_name": "Reserve and Surplus", - "is_group": 0, - "company": "Test PCV Company", - "root_type": "Liability", - "report_type": "Balance Sheet", - "account_currency": "USD", - "parent_account": "Current Liabilities - TPC", - "doctype": "Account" - }).insert(ignore_if_duplicate = True) + account = frappe.get_doc( + { + "account_name": "Reserve and Surplus", + "is_group": 0, + "company": "Test PCV Company", + "root_type": "Liability", + "report_type": "Balance Sheet", + "account_currency": "USD", + "parent_account": "Current Liabilities - TPC", + "doctype": "Account", + } + ).insert(ignore_if_duplicate=True) return account.name + def create_cost_center(cc_name): - costcenter = frappe.get_doc({ - "company": "Test PCV Company", - "cost_center_name": cc_name, - "doctype": "Cost Center", - "parent_cost_center": "Test PCV Company - TPC" - }) - costcenter.insert(ignore_if_duplicate = True) + costcenter = frappe.get_doc( + { + "company": "Test PCV Company", + "cost_center_name": cc_name, + "doctype": "Cost Center", + "parent_cost_center": "Test PCV Company - TPC", + } + ) + costcenter.insert(ignore_if_duplicate=True) return costcenter.name + test_dependencies = ["Customer", "Cost Center"] test_records = frappe.get_test_records("Period Closing Voucher") diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index 07059cb7aa3b..49aab0d0bbf0 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -23,21 +23,33 @@ def validate(self): def validate_pos_invoices(self): invalid_rows = [] for d in self.pos_transactions: - invalid_row = {'idx': d.idx} - pos_invoice = frappe.db.get_values("POS Invoice", d.pos_invoice, - ["consolidated_invoice", "pos_profile", "docstatus", "owner"], as_dict=1)[0] + invalid_row = {"idx": d.idx} + pos_invoice = frappe.db.get_values( + "POS Invoice", + d.pos_invoice, + ["consolidated_invoice", "pos_profile", "docstatus", "owner"], + as_dict=1, + )[0] if pos_invoice.consolidated_invoice: - invalid_row.setdefault('msg', []).append(_('POS Invoice is {}').format(frappe.bold("already consolidated"))) + invalid_row.setdefault("msg", []).append( + _("POS Invoice is {}").format(frappe.bold("already consolidated")) + ) invalid_rows.append(invalid_row) continue if pos_invoice.pos_profile != self.pos_profile: - invalid_row.setdefault('msg', []).append(_("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile))) + invalid_row.setdefault("msg", []).append( + _("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile)) + ) if pos_invoice.docstatus != 1: - invalid_row.setdefault('msg', []).append(_('POS Invoice is not {}').format(frappe.bold("submitted"))) + invalid_row.setdefault("msg", []).append( + _("POS Invoice is not {}").format(frappe.bold("submitted")) + ) if pos_invoice.owner != self.user: - invalid_row.setdefault('msg', []).append(_("POS Invoice isn't created by user {}").format(frappe.bold(self.owner))) + invalid_row.setdefault("msg", []).append( + _("POS Invoice isn't created by user {}").format(frappe.bold(self.owner)) + ) - if invalid_row.get('msg'): + if invalid_row.get("msg"): invalid_rows.append(invalid_row) if not invalid_rows: @@ -45,16 +57,18 @@ def validate_pos_invoices(self): error_list = [] for row in invalid_rows: - for msg in row.get('msg'): - error_list.append(_("Row #{}: {}").format(row.get('idx'), msg)) + for msg in row.get("msg"): + error_list.append(_("Row #{}: {}").format(row.get("idx"), msg)) frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True) @frappe.whitelist() def get_payment_reconciliation_details(self): - currency = frappe.get_cached_value('Company', self.company, "default_currency") - return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html", - {"data": self, "currency": currency}) + currency = frappe.get_cached_value("Company", self.company, "default_currency") + return frappe.render_template( + "erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html", + {"data": self, "currency": currency}, + ) def on_submit(self): consolidate_pos_invoices(closing_entry=self) @@ -72,29 +86,38 @@ def update_opening_entry(self, for_cancel=False): opening_entry.set_status() opening_entry.save() + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_cashiers(doctype, txt, searchfield, start, page_len, filters): - cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=['user'], as_list=1) + cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=["user"], as_list=1) return [c for c in cashiers_list] + @frappe.whitelist() def get_pos_invoices(start, end, pos_profile, user): - data = frappe.db.sql(""" + data = frappe.db.sql( + """ select name, timestamp(posting_date, posting_time) as "timestamp" from `tabPOS Invoice` where owner = %s and docstatus = 1 and pos_profile = %s and ifnull(consolidated_invoice,'') = '' - """, (user, pos_profile), as_dict=1) - - data = list(filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data)) + """, + (user, pos_profile), + as_dict=1, + ) + + 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] return data + def make_closing_entry_from_opening(opening_entry): closing_entry = frappe.new_doc("POS Closing Entry") closing_entry.pos_opening_entry = opening_entry.name @@ -107,26 +130,38 @@ def make_closing_entry_from_opening(opening_entry): closing_entry.net_total = 0 closing_entry.total_quantity = 0 - invoices = get_pos_invoices(closing_entry.period_start_date, closing_entry.period_end_date, - closing_entry.pos_profile, closing_entry.user) + invoices = get_pos_invoices( + closing_entry.period_start_date, + closing_entry.period_end_date, + closing_entry.pos_profile, + closing_entry.user, + ) pos_transactions = [] taxes = [] payments = [] for detail in opening_entry.balance_details: - payments.append(frappe._dict({ - 'mode_of_payment': detail.mode_of_payment, - 'opening_amount': detail.opening_amount, - 'expected_amount': detail.opening_amount - })) + payments.append( + frappe._dict( + { + "mode_of_payment": detail.mode_of_payment, + "opening_amount": detail.opening_amount, + "expected_amount": detail.opening_amount, + } + ) + ) for d in invoices: - pos_transactions.append(frappe._dict({ - 'pos_invoice': d.name, - 'posting_date': d.posting_date, - 'grand_total': d.grand_total, - 'customer': d.customer - })) + pos_transactions.append( + frappe._dict( + { + "pos_invoice": d.name, + "posting_date": d.posting_date, + "grand_total": d.grand_total, + "customer": d.customer, + } + ) + ) closing_entry.grand_total += flt(d.grand_total) closing_entry.net_total += flt(d.net_total) closing_entry.total_quantity += flt(d.total_qty) @@ -134,24 +169,22 @@ def make_closing_entry_from_opening(opening_entry): for t in d.taxes: existing_tax = [tx for tx in taxes if tx.account_head == t.account_head and tx.rate == t.rate] if existing_tax: - existing_tax[0].amount += flt(t.tax_amount); + existing_tax[0].amount += flt(t.tax_amount) else: - taxes.append(frappe._dict({ - 'account_head': t.account_head, - 'rate': t.rate, - 'amount': t.tax_amount - })) + taxes.append( + frappe._dict({"account_head": t.account_head, "rate": t.rate, "amount": t.tax_amount}) + ) for p in d.payments: existing_pay = [pay for pay in payments if pay.mode_of_payment == p.mode_of_payment] if existing_pay: - existing_pay[0].expected_amount += flt(p.amount); + existing_pay[0].expected_amount += flt(p.amount) else: - payments.append(frappe._dict({ - 'mode_of_payment': p.mode_of_payment, - 'opening_amount': 0, - 'expected_amount': p.amount - })) + payments.append( + frappe._dict( + {"mode_of_payment": p.mode_of_payment, "opening_amount": 0, "expected_amount": p.amount} + ) + ) closing_entry.set("pos_transactions", pos_transactions) closing_entry.set("payment_reconciliation", payments) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index 626bee0b496f..8eb28dfaa2cb 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -28,24 +28,20 @@ def test_pos_closing_entry(self): opening_entry = create_opening_entry(pos_profile, test_user.name) pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) - pos_inv1.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500 - }) + pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) pos_inv1.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() pcv_doc = make_closing_entry_from_opening(opening_entry) payment = pcv_doc.payment_reconciliation[0] - self.assertEqual(payment.mode_of_payment, 'Cash') + self.assertEqual(payment.mode_of_payment, "Cash") for d in pcv_doc.payment_reconciliation: - if d.mode_of_payment == 'Cash': + if d.mode_of_payment == "Cash": d.closing_amount = 6700 pcv_doc.submit() @@ -58,24 +54,20 @@ def test_cancelling_of_pos_closing_entry(self): opening_entry = create_opening_entry(pos_profile, test_user.name) pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) - pos_inv1.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500 - }) + pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) pos_inv1.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() pcv_doc = make_closing_entry_from_opening(opening_entry) payment = pcv_doc.payment_reconciliation[0] - self.assertEqual(payment.mode_of_payment, 'Cash') + self.assertEqual(payment.mode_of_payment, "Cash") for d in pcv_doc.payment_reconciliation: - if d.mode_of_payment == 'Cash': + if d.mode_of_payment == "Cash": d.closing_amount = 6700 pcv_doc.submit() @@ -90,29 +82,25 @@ def test_cancelling_of_pos_closing_entry(self): pcv_doc.cancel() cancelled_invoice = frappe.db.get_value( - 'POS Invoice Merge Log', {'pos_closing_entry': pcv_doc.name}, - 'consolidated_invoice' + "POS Invoice Merge Log", {"pos_closing_entry": pcv_doc.name}, "consolidated_invoice" ) - docstatus = frappe.db.get_value("Sales Invoice", cancelled_invoice, 'docstatus') + docstatus = frappe.db.get_value("Sales Invoice", cancelled_invoice, "docstatus") self.assertEqual(docstatus, 2) pos_inv1.load_from_db() - self.assertEqual(pos_inv1.status, 'Paid') + self.assertEqual(pos_inv1.status, "Paid") def init_user_and_profile(**args): - user = 'test@example.com' - test_user = frappe.get_doc('User', user) + user = "test@example.com" + test_user = frappe.get_doc("User", user) roles = ("Accounts Manager", "Accounts User", "Sales Manager") test_user.add_roles(*roles) frappe.set_user(user) pos_profile = make_pos_profile(**args) - pos_profile.append('applicable_for_users', { - 'default': 1, - 'user': user - }) + pos_profile.append("applicable_for_users", {"default": 1, "user": user}) pos_profile.save() diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 0c6e7edeb02a..b8500270d1ac 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -264,7 +264,6 @@ "print_hide": 1 }, { - "allow_on_submit": 1, "default": "0", "fieldname": "is_return", "fieldtype": "Check", @@ -1573,7 +1572,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-10-05 12:11:53.871828", + "modified": "2022-03-22 13:00:24.166684", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", @@ -1623,6 +1622,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "timeline_field": "customer", "title_field": "title", "track_changes": 1, diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 9b3b3aa4149f..94246e135b61 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -16,7 +16,11 @@ ) from erpnext.accounts.party import get_due_date, get_party_account from erpnext.stock.doctype.batch.batch import get_batch_qty, get_pos_reserved_batch_qty -from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos +from erpnext.stock.doctype.serial_no.serial_no import ( + get_delivered_serial_nos, + get_pos_reserved_serial_nos, + get_serial_nos, +) class POSInvoice(SalesInvoice): @@ -25,7 +29,9 @@ def __init__(self, *args, **kwargs): def validate(self): if not cint(self.is_pos): - frappe.throw(_("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment"))) + frappe.throw( + _("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment")) + ) # run on validate method of selling controller super(SalesInvoice, self).validate() @@ -49,11 +55,12 @@ def validate(self): self.validate_loyalty_transaction() if self.coupon_code: from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code + validate_coupon_code(self.coupon_code) def on_submit(self): # create the loyalty point ledger entry if the customer is enrolled in any loyalty program - if self.loyalty_program: + if not self.is_return and self.loyalty_program: self.make_loyalty_point_entry() elif self.is_return and self.return_against and self.loyalty_program: against_psi_doc = frappe.get_doc("POS Invoice", self.return_against) @@ -66,28 +73,32 @@ def on_submit(self): if self.coupon_code: from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count - update_coupon_code_count(self.coupon_code,'used') + + update_coupon_code_count(self.coupon_code, "used") def before_cancel(self): - if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1: + if ( + self.consolidated_invoice + and frappe.db.get_value("Sales Invoice", self.consolidated_invoice, "docstatus") == 1 + ): pos_closing_entry = frappe.get_all( "POS Invoice Reference", ignore_permissions=True, - filters={ 'pos_invoice': self.name }, + filters={"pos_invoice": self.name}, pluck="parent", - limit=1 + limit=1, ) frappe.throw( - _('You need to cancel POS Closing Entry {} to be able to cancel this document.').format( + _("You need to cancel POS Closing Entry {} to be able to cancel this document.").format( get_link_to_form("POS Closing Entry", pos_closing_entry[0]) ), - title=_('Not Allowed') + title=_("Not Allowed"), ) def on_cancel(self): # run on cancel method of selling controller super(SalesInvoice, self).on_cancel() - if self.loyalty_program: + if not self.is_return and self.loyalty_program: self.delete_loyalty_point_entry() elif self.is_return and self.return_against and self.loyalty_program: against_psi_doc = frappe.get_doc("POS Invoice", self.return_against) @@ -96,16 +107,22 @@ def on_cancel(self): if self.coupon_code: from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count - update_coupon_code_count(self.coupon_code,'cancelled') + + update_coupon_code_count(self.coupon_code, "cancelled") def check_phone_payments(self): for pay in self.payments: if pay.type == "Phone" and pay.amount >= 0: - paid_amt = frappe.db.get_value("Payment Request", + paid_amt = frappe.db.get_value( + "Payment Request", filters=dict( - reference_doctype="POS Invoice", reference_name=self.name, - mode_of_payment=pay.mode_of_payment, status="Paid"), - fieldname="grand_total") + reference_doctype="POS Invoice", + reference_name=self.name, + mode_of_payment=pay.mode_of_payment, + status="Paid", + ), + fieldname="grand_total", + ) if paid_amt and pay.amount != paid_amt: return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment)) @@ -119,52 +136,73 @@ def validate_pos_reserved_serial_nos(self, item): reserved_serial_nos = get_pos_reserved_serial_nos(filters) invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos] - bold_invalid_serial_nos = frappe.bold(', '.join(invalid_serial_nos)) + bold_invalid_serial_nos = frappe.bold(", ".join(invalid_serial_nos)) if len(invalid_serial_nos) == 1: - frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.") - .format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable")) + frappe.throw( + _( + "Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no." + ).format(item.idx, bold_invalid_serial_nos), + title=_("Item Unavailable"), + ) elif invalid_serial_nos: - frappe.throw(_("Row #{}: Serial Nos. {} have already been transacted into another POS Invoice. Please select valid serial no.") - .format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable")) + frappe.throw( + _( + "Row #{}: Serial Nos. {} have already been transacted into another POS Invoice. Please select valid serial no." + ).format(item.idx, bold_invalid_serial_nos), + title=_("Item Unavailable"), + ) def validate_pos_reserved_batch_qty(self, item): - filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no":item.batch_no} + filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no} available_batch_qty = get_batch_qty(item.batch_no, item.warehouse, item.item_code) reserved_batch_qty = get_pos_reserved_batch_qty(filters) bold_item_name = frappe.bold(item.item_name) - bold_extra_batch_qty_needed = frappe.bold(abs(available_batch_qty - reserved_batch_qty - item.qty)) + bold_extra_batch_qty_needed = frappe.bold( + abs(available_batch_qty - reserved_batch_qty - item.qty) + ) bold_invalid_batch_no = frappe.bold(item.batch_no) if (available_batch_qty - reserved_batch_qty) == 0: - frappe.throw(_("Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no.") - .format(item.idx, bold_invalid_batch_no, bold_item_name), title=_("Item Unavailable")) + frappe.throw( + _( + "Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no." + ).format(item.idx, bold_invalid_batch_no, bold_item_name), + title=_("Item Unavailable"), + ) elif (available_batch_qty - reserved_batch_qty - item.qty) < 0: - frappe.throw(_("Row #{}: Batch No. {} of item {} has less than required stock available, {} more required") - .format(item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed), title=_("Item Unavailable")) + frappe.throw( + _( + "Row #{}: Batch No. {} of item {} has less than required stock available, {} more required" + ).format( + item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed + ), + title=_("Item Unavailable"), + ) def validate_delivered_serial_nos(self, item): - serial_nos = get_serial_nos(item.serial_no) - delivered_serial_nos = frappe.db.get_list('Serial No', { - 'item_code': item.item_code, - 'name': ['in', serial_nos], - 'sales_invoice': ['is', 'set'] - }, pluck='name') + delivered_serial_nos = get_delivered_serial_nos(item.serial_no) if delivered_serial_nos: - bold_delivered_serial_nos = frappe.bold(', '.join(delivered_serial_nos)) - frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another Sales Invoice. Please select valid serial no.") - .format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable")) + bold_delivered_serial_nos = frappe.bold(", ".join(delivered_serial_nos)) + frappe.throw( + _( + "Row #{}: Serial No. {} has already been transacted into another Sales Invoice. Please select valid serial no." + ).format(item.idx, bold_delivered_serial_nos), + title=_("Item Unavailable"), + ) def validate_invalid_serial_nos(self, item): serial_nos = get_serial_nos(item.serial_no) error_msg = [] invalid_serials, msg = "", "" for serial_no in serial_nos: - if not frappe.db.exists('Serial No', serial_no): + if not frappe.db.exists("Serial No", serial_no): invalid_serials = invalid_serials + (", " if invalid_serials else "") + serial_no - msg = (_("Row #{}: Following Serial numbers for item {} are Invalid: {}").format(item.idx, frappe.bold(item.get("item_code")), frappe.bold(invalid_serials))) + msg = _("Row #{}: Following Serial numbers for item {} are Invalid: {}").format( + item.idx, frappe.bold(item.get("item_code")), frappe.bold(invalid_serials) + ) if invalid_serials: error_msg.append(msg) @@ -172,12 +210,18 @@ def validate_invalid_serial_nos(self, item): frappe.throw(error_msg, title=_("Invalid Item"), as_list=True) def validate_stock_availablility(self): - from erpnext.stock.stock_ledger import is_negative_stock_allowed + if self.is_return: + return - if self.is_return or self.docstatus != 1: + if self.docstatus.is_draft() and not frappe.db.get_value( + "POS Profile", self.pos_profile, "validate_stock_on_save" + ): return - for d in self.get('items'): - is_service_item = not (frappe.db.get_value('Item', d.get('item_code'), 'is_stock_item')) + + from erpnext.stock.stock_ledger import is_negative_stock_allowed + + for d in self.get("items"): + is_service_item = not (frappe.db.get_value("Item", d.get("item_code"), "is_stock_item")) if is_service_item: return if d.serial_no: @@ -192,13 +236,25 @@ def validate_stock_availablility(self): available_stock, is_stock_item = get_stock_availability(d.item_code, d.warehouse) - item_code, warehouse, qty = frappe.bold(d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty) + item_code, warehouse, qty = ( + frappe.bold(d.item_code), + frappe.bold(d.warehouse), + frappe.bold(d.qty), + ) if flt(available_stock) <= 0: - frappe.throw(_('Row #{}: Item Code: {} is not available under warehouse {}.') - .format(d.idx, item_code, warehouse), title=_("Item Unavailable")) + frappe.throw( + _("Row #{}: Item Code: {} is not available under warehouse {}.").format( + d.idx, item_code, warehouse + ), + title=_("Item Unavailable"), + ) elif flt(available_stock) < flt(d.qty): - frappe.throw(_('Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.') - .format(d.idx, item_code, warehouse, available_stock), title=_("Item Unavailable")) + frappe.throw( + _( + "Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}." + ).format(d.idx, item_code, warehouse, available_stock), + title=_("Item Unavailable"), + ) def validate_serialised_or_batched_item(self): error_msg = [] @@ -212,16 +268,21 @@ def validate_serialised_or_batched_item(self): item_code = frappe.bold(d.item_code) serial_nos = get_serial_nos(d.serial_no) if serialized and batched and (no_batch_selected or no_serial_selected): - msg = (_('Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction.') - .format(d.idx, item_code)) + msg = _( + "Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction." + ).format(d.idx, item_code) elif serialized and no_serial_selected: - msg = (_('Row #{}: No serial number selected against item: {}. Please select one or remove it to complete transaction.') - .format(d.idx, item_code)) + msg = _( + "Row #{}: No serial number selected against item: {}. Please select one or remove it to complete transaction." + ).format(d.idx, item_code) elif batched and no_batch_selected: - msg = (_('Row #{}: No batch selected against item: {}. Please select a batch or remove it to complete transaction.') - .format(d.idx, item_code)) + msg = _( + "Row #{}: No batch selected against item: {}. Please select a batch or remove it to complete transaction." + ).format(d.idx, item_code) elif serialized and not no_serial_selected and len(serial_nos) != d.qty: - msg = (_("Row #{}: You must select {} serial numbers for item {}.").format(d.idx, frappe.bold(cint(d.qty)), item_code)) + msg = _("Row #{}: You must select {} serial numbers for item {}.").format( + d.idx, frappe.bold(cint(d.qty)), item_code + ) if msg: error_msg.append(msg) @@ -230,18 +291,22 @@ def validate_serialised_or_batched_item(self): frappe.throw(error_msg, title=_("Invalid Item"), as_list=True) def validate_return_items_qty(self): - if not self.get("is_return"): return + if not self.get("is_return"): + return for d in self.get("items"): if d.get("qty") > 0: frappe.throw( - _("Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return.") - .format(d.idx, frappe.bold(d.item_code)), title=_("Invalid Item") + _( + "Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return." + ).format(d.idx, frappe.bold(d.item_code)), + title=_("Invalid Item"), ) if d.get("serial_no"): serial_nos = get_serial_nos(d.serial_no) for sr in serial_nos: - serial_no_exists = frappe.db.sql(""" + serial_no_exists = frappe.db.sql( + """ SELECT name FROM `tabPOS Invoice Item` WHERE @@ -251,14 +316,17 @@ def validate_return_items_qty(self): or serial_no like %s or serial_no like %s ) - """, (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%')) + """, + (self.return_against, sr, sr + "\n%", "%\n" + sr, "%\n" + sr + "\n%"), + ) if not serial_no_exists: bold_return_against = frappe.bold(self.return_against) bold_serial_no = frappe.bold(sr) frappe.throw( - _("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}") - .format(d.idx, bold_serial_no, bold_return_against) + _( + "Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}" + ).format(d.idx, bold_serial_no, bold_return_against) ) def validate_mode_of_payment(self): @@ -266,16 +334,25 @@ def validate_mode_of_payment(self): frappe.throw(_("At least one mode of payment is required for POS invoice.")) def validate_change_account(self): - if self.change_amount and self.account_for_change_amount and \ - frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company: - frappe.throw(_("The selected change account {} doesn't belongs to Company {}.").format(self.account_for_change_amount, self.company)) + if ( + self.change_amount + and self.account_for_change_amount + and frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company + ): + frappe.throw( + _("The selected change account {} doesn't belongs to Company {}.").format( + self.account_for_change_amount, self.company + ) + ) def validate_change_amount(self): grand_total = flt(self.rounded_total) or flt(self.grand_total) base_grand_total = flt(self.base_rounded_total) or flt(self.base_grand_total) if not flt(self.change_amount) and grand_total < flt(self.paid_amount): self.change_amount = flt(self.paid_amount - grand_total + flt(self.write_off_amount)) - self.base_change_amount = flt(self.base_paid_amount) - base_grand_total + flt(self.base_write_off_amount) + self.base_change_amount = ( + flt(self.base_paid_amount) - base_grand_total + flt(self.base_write_off_amount) + ) if flt(self.change_amount) and not self.account_for_change_amount: frappe.msgprint(_("Please enter Account for Change Amount"), raise_exception=1) @@ -295,8 +372,12 @@ def validate_payment_amount(self): frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) def validate_loyalty_transaction(self): - if self.redeem_loyalty_points and (not self.loyalty_redemption_account or not self.loyalty_redemption_cost_center): - expense_account, cost_center = frappe.db.get_value('Loyalty Program', self.loyalty_program, ["expense_account", "cost_center"]) + if self.redeem_loyalty_points and ( + not self.loyalty_redemption_account or not self.loyalty_redemption_cost_center + ): + expense_account, cost_center = frappe.db.get_value( + "Loyalty Program", self.loyalty_program, ["expense_account", "cost_center"] + ) if not self.loyalty_redemption_account: self.loyalty_redemption_account = expense_account if not self.loyalty_redemption_cost_center: @@ -307,8 +388,8 @@ def validate_loyalty_transaction(self): def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): - if self.get('amended_from'): - self.status = 'Draft' + if self.get("amended_from"): + self.status = "Draft" return if not status: @@ -317,19 +398,35 @@ def set_status(self, update=False, status=None, update_modified=True): elif self.docstatus == 1: if self.consolidated_invoice: self.status = "Consolidated" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + elif ( + flt(self.outstanding_amount) > 0 + and getdate(self.due_date) < getdate(nowdate()) + and self.is_discounted + and self.get_discounting_status() == "Disbursed" + ): self.status = "Overdue and Discounted" elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()): self.status = "Overdue" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + elif ( + flt(self.outstanding_amount) > 0 + and getdate(self.due_date) >= getdate(nowdate()) + and self.is_discounted + and self.get_discounting_status() == "Disbursed" + ): self.status = "Unpaid and Discounted" elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()): self.status = "Unpaid" - elif flt(self.outstanding_amount) <= 0 and self.is_return == 0 and frappe.db.get_value('POS Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + elif ( + flt(self.outstanding_amount) <= 0 + and self.is_return == 0 + and frappe.db.get_value( + "POS Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} + ) + ): self.status = "Credit Note Issued" elif self.is_return == 1: self.status = "Return" - elif flt(self.outstanding_amount)<=0: + elif flt(self.outstanding_amount) <= 0: self.status = "Paid" else: self.status = "Submitted" @@ -337,22 +434,23 @@ def set_status(self, update=False, status=None, update_modified=True): self.status = "Draft" if update: - self.db_set('status', self.status, update_modified = update_modified) + self.db_set("status", self.status, update_modified=update_modified) def set_pos_fields(self, for_validate=False): """Set retail related fields from POS Profiles""" from erpnext.stock.get_item_details import get_pos_profile, get_pos_profile_item_details + if not self.pos_profile: pos_profile = get_pos_profile(self.company) or {} if not pos_profile: frappe.throw(_("No POS Profile found. Please create a New POS Profile first")) - self.pos_profile = pos_profile.get('name') + self.pos_profile = pos_profile.get("name") profile = {} if self.pos_profile: - profile = frappe.get_doc('POS Profile', self.pos_profile) + profile = frappe.get_doc("POS Profile", self.pos_profile) - if not self.get('payments') and not for_validate: + if not self.get("payments") and not for_validate: update_multi_mode_option(self, profile) if self.is_return and not for_validate: @@ -362,35 +460,55 @@ def set_pos_fields(self, for_validate=False): if not for_validate and not self.customer: self.customer = profile.customer - self.account_for_change_amount = profile.get('account_for_change_amount') or self.account_for_change_amount - self.set_warehouse = profile.get('warehouse') or self.set_warehouse - - for fieldname in ('currency', 'letter_head', 'tc_name', - 'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges', - 'write_off_cost_center', 'apply_discount_on', 'cost_center', 'tax_category', - 'ignore_pricing_rule', 'company_address', 'update_stock'): - if not for_validate: - self.set(fieldname, profile.get(fieldname)) + self.account_for_change_amount = ( + profile.get("account_for_change_amount") or self.account_for_change_amount + ) + self.set_warehouse = profile.get("warehouse") or self.set_warehouse + + for fieldname in ( + "currency", + "letter_head", + "tc_name", + "company", + "select_print_heading", + "write_off_account", + "taxes_and_charges", + "write_off_cost_center", + "apply_discount_on", + "cost_center", + "tax_category", + "ignore_pricing_rule", + "company_address", + "update_stock", + ): + if not for_validate: + self.set(fieldname, profile.get(fieldname)) if self.customer: customer_price_list, customer_group, customer_currency = frappe.db.get_value( - "Customer", self.customer, ['default_price_list', 'customer_group', 'default_currency'] + "Customer", self.customer, ["default_price_list", "customer_group", "default_currency"] + ) + customer_group_price_list = frappe.db.get_value( + "Customer Group", customer_group, "default_price_list" + ) + selling_price_list = ( + customer_price_list or customer_group_price_list or profile.get("selling_price_list") ) - customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list') - selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list') - if customer_currency != profile.get('currency'): - self.set('currency', customer_currency) + if customer_currency != profile.get("currency"): + self.set("currency", customer_currency) else: - selling_price_list = profile.get('selling_price_list') + selling_price_list = profile.get("selling_price_list") if selling_price_list: - self.set('selling_price_list', selling_price_list) + self.set("selling_price_list", selling_price_list) # set pos values in items for item in self.get("items"): - if item.get('item_code'): - profile_details = get_pos_profile_item_details(profile.get("company"), frappe._dict(item.as_dict()), profile) + if item.get("item_code"): + profile_details = get_pos_profile_item_details( + profile.get("company"), frappe._dict(item.as_dict()), profile + ) for fname, val in profile_details.items(): if (not for_validate) or (for_validate and not item.get(fname)): item.set(fname, val) @@ -404,7 +522,9 @@ def set_pos_fields(self, for_validate=False): self.set_taxes() if not self.account_for_change_amount: - self.account_for_change_amount = frappe.get_cached_value('Company', self.company, 'default_cash_account') + self.account_for_change_amount = frappe.get_cached_value( + "Company", self.company, "default_cash_account" + ) return profile @@ -414,27 +534,29 @@ def set_missing_values(self, for_validate=False): if not self.debit_to: self.debit_to = get_party_account("Customer", self.customer, self.company) - self.party_account_currency = frappe.db.get_value("Account", self.debit_to, "account_currency", cache=True) + self.party_account_currency = frappe.db.get_value( + "Account", self.debit_to, "account_currency", cache=True + ) 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) print_format = profile.get("print_format") if profile else None - if not print_format and not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')): - print_format = 'POS Invoice' + if not print_format and not cint(frappe.db.get_value("Print Format", "POS Invoice", "disabled")): + print_format = "POS Invoice" if profile: return { "print_format": print_format, "campaign": profile.get("campaign"), - "allow_print_before_pay": profile.get("allow_print_before_pay") + "allow_print_before_pay": profile.get("allow_print_before_pay"), } @frappe.whitelist() def reset_mode_of_payments(self): if self.pos_profile: - pos_profile = frappe.get_cached_doc('POS Profile', self.pos_profile) + pos_profile = frappe.get_cached_doc("POS Profile", self.pos_profile) update_multi_mode_option(self, pos_profile) self.paid_amount = 0 @@ -463,9 +585,13 @@ def create_payment_request(self): return pay_req def get_new_payment_request(self, mop): - payment_gateway_account = frappe.db.get_value("Payment Gateway Account", { - "payment_account": mop.account, - }, ["name"]) + payment_gateway_account = frappe.db.get_value( + "Payment Gateway Account", + { + "payment_account": mop.account, + }, + ["name"], + ) args = { "dt": "POS Invoice", @@ -476,36 +602,40 @@ def get_new_payment_request(self, mop): "payment_request_type": "Inward", "party_type": "Customer", "party": self.customer, - "return_doc": True + "return_doc": True, } return make_payment_request(**args) def get_existing_payment_request(self, pay): - payment_gateway_account = frappe.db.get_value("Payment Gateway Account", { - "payment_account": pay.account, - }, ["name"]) - - args = { - 'doctype': 'Payment Request', - 'reference_doctype': 'POS Invoice', - 'reference_name': self.name, - 'payment_gateway_account': payment_gateway_account, - 'email_to': self.contact_mobile + payment_gateway_account = frappe.db.get_value( + "Payment Gateway Account", + { + "payment_account": pay.account, + }, + ["name"], + ) + + filters = { + "reference_doctype": "POS Invoice", + "reference_name": self.name, + "payment_gateway_account": payment_gateway_account, + "email_to": self.contact_mobile, } - pr = frappe.db.exists(args) + pr = frappe.db.get_value("Payment Request", filters=filters) if pr: - return frappe.get_doc('Payment Request', pr[0][0]) + return frappe.get_doc("Payment Request", pr) + @frappe.whitelist() def get_stock_availability(item_code, warehouse): - if frappe.db.get_value('Item', item_code, 'is_stock_item'): + if frappe.db.get_value("Item", item_code, "is_stock_item"): is_stock_item = True bin_qty = get_bin_qty(item_code, warehouse) pos_sales_qty = get_pos_reserved_qty(item_code, warehouse) return bin_qty - pos_sales_qty, is_stock_item else: is_stock_item = False - if frappe.db.exists('Product Bundle', item_code): + if frappe.db.exists("Product Bundle", item_code): return get_bundle_availability(item_code, warehouse), is_stock_item else: # Is a service item @@ -513,7 +643,7 @@ def get_stock_availability(item_code, warehouse): def get_bundle_availability(bundle_item_code, warehouse): - product_bundle = frappe.get_doc('Product Bundle', bundle_item_code) + product_bundle = frappe.get_doc("Product Bundle", bundle_item_code) bundle_bin_qty = 1000000 for item in product_bundle.items: @@ -528,68 +658,87 @@ def get_bundle_availability(bundle_item_code, warehouse): pos_sales_qty = get_pos_reserved_qty(bundle_item_code, warehouse) return bundle_bin_qty - pos_sales_qty + def get_bin_qty(item_code, warehouse): - bin_qty = frappe.db.sql("""select actual_qty from `tabBin` + bin_qty = frappe.db.sql( + """select actual_qty from `tabBin` where item_code = %s and warehouse = %s - limit 1""", (item_code, warehouse), as_dict=1) + limit 1""", + (item_code, warehouse), + as_dict=1, + ) return bin_qty[0].actual_qty or 0 if bin_qty else 0 + def get_pos_reserved_qty(item_code, warehouse): - reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty + reserved_qty = frappe.db.sql( + """select sum(p_item.qty) as qty from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item where p.name = p_item.parent and ifnull(p.consolidated_invoice, '') = '' and p_item.docstatus = 1 and p_item.item_code = %s and p_item.warehouse = %s - """, (item_code, warehouse), as_dict=1) + """, + (item_code, warehouse), + as_dict=1, + ) return reserved_qty[0].qty or 0 if reserved_qty else 0 + @frappe.whitelist() def make_sales_return(source_name, target_doc=None): from erpnext.controllers.sales_and_purchase_return import make_return_doc + return make_return_doc("POS Invoice", source_name, target_doc) + @frappe.whitelist() def make_merge_log(invoices): import json - if isinstance(invoices, str): invoices = json.loads(invoices) if len(invoices) == 0: - frappe.throw(_('Atleast one invoice has to be selected.')) + frappe.throw(_("Atleast one invoice has to be selected.")) merge_log = frappe.new_doc("POS Invoice Merge Log") merge_log.posting_date = getdate(nowdate()) for inv in invoices: - inv_data = frappe.db.get_values("POS Invoice", inv.get('name'), - ["customer", "posting_date", "grand_total"], as_dict=1)[0] + inv_data = frappe.db.get_values( + "POS Invoice", inv.get("name"), ["customer", "posting_date", "grand_total"], as_dict=1 + )[0] merge_log.customer = inv_data.customer - merge_log.append("pos_invoices", { - 'pos_invoice': inv.get('name'), - 'customer': inv_data.customer, - 'posting_date': inv_data.posting_date, - 'grand_total': inv_data.grand_total - }) - - if merge_log.get('pos_invoices'): + merge_log.append( + "pos_invoices", + { + "pos_invoice": inv.get("name"), + "customer": inv_data.customer, + "posting_date": inv_data.posting_date, + "grand_total": inv_data.grand_total, + }, + ) + + if merge_log.get("pos_invoices"): return merge_log.as_dict() + def add_return_modes(doc, pos_profile): def append_payment(payment_mode): - payment = doc.append('payments', {}) + payment = doc.append("payments", {}) payment.default = payment_mode.default payment.mode_of_payment = payment_mode.parent payment.account = payment_mode.default_account payment.type = payment_mode.type - for pos_payment_method in pos_profile.get('payments'): + for pos_payment_method in pos_profile.get("payments"): pos_payment_method = pos_payment_method.as_dict() mode_of_payment = pos_payment_method.mode_of_payment - if pos_payment_method.allow_in_returns and not [d for d in doc.get('payments') if d.mode_of_payment == mode_of_payment]: + if pos_payment_method.allow_in_returns and not [ + d for d in doc.get("payments") if d.mode_of_payment == mode_of_payment + ]: payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company) append_payment(payment_mode[0]) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index cf8affdd010c..70f128e0e39a 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -35,29 +35,34 @@ def test_timestamp_change(self): w2 = frappe.get_doc(w.doctype, w.name) import time + time.sleep(1) w.save() import time + time.sleep(1) self.assertRaises(frappe.TimestampMismatchError, w2.save) def test_change_naming_series(self): inv = create_pos_invoice(do_not_submit=1) - inv.naming_series = 'TEST-' + inv.naming_series = "TEST-" self.assertRaises(frappe.CannotChangeConstantError, inv.save) def test_discount_and_inclusive_tax(self): inv = create_pos_invoice(qty=100, rate=50, do_not_save=1) - inv.append("taxes", { - "charge_type": "On Net Total", - "account_head": "_Test Account Service Tax - _TC", - "cost_center": "_Test Cost Center - _TC", - "description": "Service Tax", - "rate": 14, - 'included_in_print_rate': 1 - }) + inv.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 14, + "included_in_print_rate": 1, + }, + ) inv.insert() self.assertEqual(inv.net_total, 4385.96) @@ -66,7 +71,7 @@ def test_discount_and_inclusive_tax(self): inv.reload() inv.discount_amount = 100 - inv.apply_discount_on = 'Net Total' + inv.apply_discount_on = "Net Total" inv.payment_schedule = [] inv.save() @@ -77,7 +82,7 @@ def test_discount_and_inclusive_tax(self): inv.reload() inv.discount_amount = 100 - inv.apply_discount_on = 'Grand Total' + inv.apply_discount_on = "Grand Total" inv.payment_schedule = [] inv.save() @@ -93,14 +98,17 @@ def test_tax_calculation_with_multiple_items(self): item_row_copy.qty = qty inv.append("items", item_row_copy) - inv.append("taxes", { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "rate": 19 - }) + inv.append( + "taxes", + { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "rate": 19, + }, + ) inv.insert() self.assertEqual(inv.net_total, 4600) @@ -115,10 +123,10 @@ def test_tax_calculation_with_item_tax_template(self): item_row = inv.get("items")[0] add_items = [ - (54, '_Test Account Excise Duty @ 12 - _TC'), - (288, '_Test Account Excise Duty @ 15 - _TC'), - (144, '_Test Account Excise Duty @ 20 - _TC'), - (430, '_Test Item Tax Template 1 - _TC') + (54, "_Test Account Excise Duty @ 12 - _TC"), + (288, "_Test Account Excise Duty @ 15 - _TC"), + (144, "_Test Account Excise Duty @ 20 - _TC"), + (430, "_Test Item Tax Template 1 - _TC"), ] for qty, item_tax_template in add_items: item_row_copy = copy.deepcopy(item_row) @@ -126,30 +134,39 @@ def test_tax_calculation_with_item_tax_template(self): item_row_copy.item_tax_template = item_tax_template inv.append("items", item_row_copy) - inv.append("taxes", { - "account_head": "_Test Account Excise Duty - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Excise Duty", - "doctype": "Sales Taxes and Charges", - "rate": 11 - }) - inv.append("taxes", { - "account_head": "_Test Account Education Cess - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Education Cess", - "doctype": "Sales Taxes and Charges", - "rate": 0 - }) - inv.append("taxes", { - "account_head": "_Test Account S&H Education Cess - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "S&H Education Cess", - "doctype": "Sales Taxes and Charges", - "rate": 3 - }) + inv.append( + "taxes", + { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Sales Taxes and Charges", + "rate": 11, + }, + ) + inv.append( + "taxes", + { + "account_head": "_Test Account Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 0, + }, + ) + inv.append( + "taxes", + { + "account_head": "_Test Account S&H Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 3, + }, + ) inv.insert() self.assertEqual(inv.net_total, 4600) @@ -179,14 +196,17 @@ def test_tax_calculation_with_multiple_items_and_discount(self): inv.apply_discount_on = "Net Total" inv.discount_amount = 75.0 - inv.append("taxes", { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "rate": 24 - }) + inv.append( + "taxes", + { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "rate": 24, + }, + ) inv.insert() self.assertEqual(inv.total, 975) @@ -198,11 +218,15 @@ def test_tax_calculation_with_multiple_items_and_discount(self): self.assertEqual(inv.grand_total, 1116.0) def test_pos_returns_with_repayment(self): - pos = create_pos_invoice(qty = 10, do_not_save=True) + pos = create_pos_invoice(qty=10, do_not_save=True) - pos.set('payments', []) - pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500}) - pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1}) + pos.set("payments", []) + pos.append( + "payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 500} + ) + pos.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 500, "default": 1} + ) pos.insert() pos.submit() @@ -211,25 +235,39 @@ def test_pos_returns_with_repayment(self): pos_return.insert() pos_return.submit() - self.assertEqual(pos_return.get('payments')[0].amount, -500) - self.assertEqual(pos_return.get('payments')[1].amount, -500) + self.assertEqual(pos_return.get("payments")[0].amount, -500) + 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(company='_Test Company', - target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) serial_nos = get_serial_nos(se.get("items")[0].serial_no) - pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', - account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', - expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', - item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + do_not_save=1, + ) pos.get("items")[0].serial_no = serial_nos[0] - pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1}) + pos.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1} + ) pos.insert() pos.submit() @@ -238,24 +276,39 @@ def test_pos_return_for_serialized_item(self): pos_return.insert() pos_return.submit() - self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0]) + self.assertEqual(pos_return.get("items")[0].serial_no, serial_nos[0]) 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(company='_Test Company', - target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) serial_nos = get_serial_nos(se.get("items")[0].serial_no) - pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', - account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', - expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', - item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1) + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + qty=2, + rate=1000, + do_not_save=1, + ) pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1] - pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1}) + pos.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1} + ) pos.insert() pos.submit() @@ -263,24 +316,34 @@ def test_partial_pos_returns(self): pos_return1 = make_sales_return(pos.name) # partial return 1 - pos_return1.get('items')[0].qty = -1 - pos_return1.get('items')[0].serial_no = serial_nos[0] + pos_return1.get("items")[0].qty = -1 + pos_return1.get("items")[0].serial_no = serial_nos[0] pos_return1.insert() pos_return1.submit() # partial return 2 pos_return2 = make_sales_return(pos.name) - self.assertEqual(pos_return2.get('items')[0].qty, -1) - self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1]) + self.assertEqual(pos_return2.get("items")[0].qty, -1) + self.assertEqual(pos_return2.get("items")[0].serial_no, serial_nos[1]) def test_pos_change_amount(self): - pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC", - income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105, - cost_center = "Main - _TC", do_not_save=True) + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + rate=105, + cost_center="Main - _TC", + do_not_save=True, + ) - pos.set('payments', []) - 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}) + pos.set("payments", []) + 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} + ) pos.insert() pos.submit() @@ -298,29 +361,53 @@ 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(company='_Test Company', - target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) serial_nos = get_serial_nos(se.get("items")[0].serial_no) - pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', - account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', - expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', - item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + do_not_save=1, + ) pos.get("items")[0].serial_no = serial_nos[0] - pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000}) + pos.append( + "payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 1000} + ) pos.insert() pos.submit() - pos2 = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', - account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', - expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', - item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos2 = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + do_not_save=1, + ) pos2.get("items")[0].serial_no = serial_nos[0] - pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000}) + pos2.append( + "payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 1000} + ) pos2.insert() self.assertRaises(frappe.ValidationError, pos2.submit) @@ -329,27 +416,50 @@ 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(company='_Test Company', - target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) serial_nos = get_serial_nos(se.get("items")[0].serial_no) - si = create_sales_invoice(company='_Test Company', debit_to='Debtors - _TC', - account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', - expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', - item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + si = create_sales_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + do_not_save=1, + ) si.get("items")[0].serial_no = serial_nos[0] + si.update_stock = 1 si.insert() si.submit() - pos2 = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', - account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', - expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', - item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos2 = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + do_not_save=1, + ) pos2.get("items")[0].serial_no = serial_nos[0] - pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000}) + pos2.append( + "payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 1000} + ) pos2.insert() self.assertRaises(frappe.ValidationError, pos2.submit) @@ -357,17 +467,30 @@ def test_delivered_serialized_item_transaction(self): def test_invalid_serial_no_validation(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item - se = make_serialized_item(company='_Test Company', - target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') - serial_nos = se.get("items")[0].serial_no + 'wrong' - - pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', - account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', - expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', - item=se.get("items")[0].item_code, rate=1000, qty=2, do_not_save=1) + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) + serial_nos = se.get("items")[0].serial_no + "wrong" + + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=2, + do_not_save=1, + ) - pos.get('items')[0].has_serial_no = 1 - pos.get('items')[0].serial_no = serial_nos + pos.get("items")[0].has_serial_no = 1 + pos.get("items")[0].serial_no = serial_nos pos.insert() self.assertRaises(frappe.ValidationError, pos.submit) @@ -379,20 +502,31 @@ def test_loyalty_points(self): 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") - before_lp_details = get_loyalty_program_details_with_points("Test Loyalty Customer", company="_Test Company", 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" + ) inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000) - lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'POS Invoice', 'invoice': inv.name, 'customer': inv.customer}) - after_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program) + lpe = frappe.get_doc( + "Loyalty Point Entry", + {"invoice_type": "POS Invoice", "invoice": inv.name, "customer": inv.customer}, + ) + after_lp_details = get_loyalty_program_details_with_points( + inv.customer, company=inv.company, loyalty_program=inv.loyalty_program + ) - self.assertEqual(inv.get('loyalty_program'), "Test Single Loyalty") + self.assertEqual(inv.get("loyalty_program"), "Test Single Loyalty") self.assertEqual(lpe.loyalty_points, 10) self.assertEqual(after_lp_details.loyalty_points, before_lp_details.loyalty_points + 10) inv.cancel() - after_cancel_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program) + after_cancel_lp_details = get_loyalty_program_details_with_points( + inv.customer, company=inv.company, loyalty_program=inv.loyalty_program + ) self.assertEqual(after_cancel_lp_details.loyalty_points, before_lp_details.loyalty_points) def test_loyalty_points_redeemption(self): @@ -403,17 +537,24 @@ def test_loyalty_points_redeemption(self): # add 10 loyalty points create_pos_invoice(customer="Test Loyalty Customer", rate=10000) - before_lp_details = get_loyalty_program_details_with_points("Test Loyalty Customer", company="_Test Company", 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" + ) inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1) inv.redeem_loyalty_points = 1 inv.loyalty_points = before_lp_details.loyalty_points inv.loyalty_amount = inv.loyalty_points * before_lp_details.conversion_factor - inv.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 10000 - inv.loyalty_amount}) + inv.append( + "payments", + {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 10000 - inv.loyalty_amount}, + ) inv.paid_amount = 10000 inv.submit() - after_redeem_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program) + after_redeem_lp_details = get_loyalty_program_details_with_points( + inv.customer, company=inv.company, loyalty_program=inv.loyalty_program + ) self.assertEqual(after_redeem_lp_details.loyalty_points, 9) def test_merging_into_sales_invoice_with_discount(self): @@ -427,21 +568,19 @@ def test_merging_into_sales_invoice_with_discount(self): frappe.db.sql("delete from `tabPOS Invoice`") test_user, pos_profile = init_user_and_profile() pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1) - pos_inv.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 270 - }) + pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 270}) 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() 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): @@ -455,38 +594,42 @@ def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self): frappe.db.sql("delete from `tabPOS Invoice`") test_user, pos_profile = init_user_and_profile() pos_inv = create_pos_invoice(rate=300, do_not_submit=1) - pos_inv.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300 - }) - pos_inv.append('taxes', { - "charge_type": "On Net Total", - "account_head": "_Test Account Service Tax - _TC", - "cost_center": "_Test Cost Center - _TC", - "description": "Service Tax", - "rate": 14, - 'included_in_print_rate': 1 - }) + pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300}) + pos_inv.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 14, + "included_in_print_rate": 1, + }, + ) pos_inv.submit() pos_inv2 = create_pos_invoice(rate=300, qty=2, do_not_submit=1) pos_inv2.additional_discount_percentage = 10 - pos_inv2.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 540 - }) - pos_inv2.append('taxes', { - "charge_type": "On Net Total", - "account_head": "_Test Account Service Tax - _TC", - "cost_center": "_Test Cost Center - _TC", - "description": "Service Tax", - "rate": 14, - 'included_in_print_rate': 1 - }) + pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 540}) + pos_inv2.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 14, + "included_in_print_rate": 1, + }, + ) pos_inv2.submit() 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): @@ -506,64 +649,75 @@ def test_merging_with_validate_selling_price(self): frappe.db.sql("delete from `tabPOS Invoice`") test_user, pos_profile = init_user_and_profile() pos_inv = create_pos_invoice(item=item, rate=300, do_not_submit=1) - pos_inv.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300 - }) - pos_inv.append('taxes', { - "charge_type": "On Net Total", - "account_head": "_Test Account Service Tax - _TC", - "cost_center": "_Test Cost Center - _TC", - "description": "Service Tax", - "rate": 14, - 'included_in_print_rate': 1 - }) + pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300}) + pos_inv.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 14, + "included_in_print_rate": 1, + }, + ) self.assertRaises(frappe.ValidationError, pos_inv.submit) pos_inv2 = create_pos_invoice(item=item, rate=400, do_not_submit=1) - pos_inv2.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400 - }) - pos_inv2.append('taxes', { - "charge_type": "On Net Total", - "account_head": "_Test Account Service Tax - _TC", - "cost_center": "_Test Cost Center - _TC", - "description": "Service Tax", - "rate": 14, - 'included_in_print_rate': 1 - }) + pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 400}) + pos_inv2.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 14, + "included_in_print_rate": 1, + }, + ) pos_inv2.submit() 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_item_qty_validation(self): from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( create_batch_item_with_batch, ) - create_batch_item_with_batch('_BATCH ITEM', 'TestBatch 01') - item = frappe.get_doc('Item', '_BATCH ITEM') - batch = frappe.get_doc('Batch', 'TestBatch 01') + + create_batch_item_with_batch("_BATCH ITEM", "TestBatch 01") + item = frappe.get_doc("Item", "_BATCH ITEM") + batch = frappe.get_doc("Batch", "TestBatch 01") batch.submit() - item.batch_no = 'TestBatch 01' + item.batch_no = "TestBatch 01" item.save() - se = make_stock_entry(target="_Test Warehouse - _TC", item_code="_BATCH ITEM", qty=2, basic_rate=100, batch_no='TestBatch 01') + se = make_stock_entry( + target="_Test Warehouse - _TC", + item_code="_BATCH ITEM", + qty=2, + basic_rate=100, + batch_no="TestBatch 01", + ) pos_inv1 = create_pos_invoice(item=item.name, rate=300, qty=1, do_not_submit=1) - pos_inv1.items[0].batch_no = 'TestBatch 01' + pos_inv1.items[0].batch_no = "TestBatch 01" pos_inv1.save() pos_inv1.submit() pos_inv2 = create_pos_invoice(item=item.name, rate=300, qty=2, do_not_submit=1) - pos_inv2.items[0].batch_no = 'TestBatch 01' + pos_inv2.items[0].batch_no = "TestBatch 01" pos_inv2.save() self.assertRaises(frappe.ValidationError, pos_inv2.submit) - #teardown + # teardown pos_inv1.reload() pos_inv1.cancel() pos_inv1.delete() @@ -577,12 +731,14 @@ def test_pos_batch_item_qty_validation(self): def test_ignore_pricing_rule(self): from erpnext.accounts.doctype.pricing_rule.test_pricing_rule import make_pricing_rule - item_price = frappe.get_doc({ - 'doctype': 'Item Price', - 'item_code': '_Test Item', - 'price_list': '_Test Price List', - 'price_list_rate': '450', - }) + item_price = frappe.get_doc( + { + "doctype": "Item Price", + "item_code": "_Test Item", + "price_list": "_Test Price List", + "price_list_rate": "450", + } + ) item_price.insert() pr = make_pricing_rule(selling=1, priority=5, discount_percentage=10) pr.save() @@ -610,6 +766,76 @@ def test_ignore_pricing_rule(self): pos_inv.delete() pr.delete() + def test_delivered_serial_no_case(self): + from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import ( + 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") + try: + se = make_serialized_item() + serial_no = get_serial_nos(se.get("items")[0].serial_no)[0] + + dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no) + + delivery_document_no = frappe.db.get_value("Serial No", serial_no, "delivery_document_no") + self.assertEquals(delivery_document_no, dn.name) + + init_user_and_profile() + + pos_inv = create_pos_invoice( + item_code="_Test Serialized Item With Series", + serial_no=serial_no, + qty=1, + rate=100, + do_not_submit=True, + ) + + self.assertRaises(frappe.ValidationError, pos_inv.submit) + + finally: + frappe.db.rollback(save_point="before_test_delivered_serial_no_case") + frappe.set_user("Administrator") + + def test_returned_serial_no_case(self): + from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import ( + init_user_and_profile, + ) + from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos + 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_returned_serial_no_case") + try: + se = make_serialized_item() + serial_no = get_serial_nos(se.get("items")[0].serial_no)[0] + + init_user_and_profile() + + pos_inv = create_pos_invoice( + item_code="_Test Serialized Item With Series", + serial_no=serial_no, + qty=1, + rate=100, + ) + + pos_return = make_sales_return(pos_inv.name) + pos_return.flags.ignore_validate = True + pos_return.insert() + pos_return.submit() + + pos_reserved_serial_nos = get_pos_reserved_serial_nos( + {"item_code": "_Test Serialized Item With Series", "warehouse": "_Test Warehouse - _TC"} + ) + self.assertTrue(serial_no not in pos_reserved_serial_nos) + + finally: + frappe.db.rollback(save_point="before_test_returned_serial_no_case") + frappe.set_user("Administrator") + def create_pos_invoice(**args): args = frappe._dict(args) @@ -633,23 +859,26 @@ def create_pos_invoice(**args): pos_inv.debit_to = args.debit_to or "Debtors - _TC" pos_inv.is_return = args.is_return pos_inv.return_against = args.return_against - pos_inv.currency=args.currency or "INR" + pos_inv.currency = args.currency or "INR" pos_inv.conversion_rate = args.conversion_rate or 1 pos_inv.account_for_change_amount = args.account_for_change_amount or "Cash - _TC" pos_inv.set_missing_values() - pos_inv.append("items", { - "item_code": args.item or args.item_code or "_Test Item", - "warehouse": args.warehouse or "_Test Warehouse - _TC", - "qty": args.qty or 1, - "rate": args.rate if args.get("rate") is not None else 100, - "income_account": args.income_account or "Sales - _TC", - "expense_account": args.expense_account or "Cost of Goods Sold - _TC", - "cost_center": args.cost_center or "_Test Cost Center - _TC", - "serial_no": args.serial_no, - "batch_no": args.batch_no - }) + pos_inv.append( + "items", + { + "item_code": args.item or args.item_code or "_Test Item", + "warehouse": args.warehouse or "_Test Warehouse - _TC", + "qty": args.qty or 1, + "rate": args.rate if args.get("rate") is not None else 100, + "income_account": args.income_account or "Sales - _TC", + "expense_account": args.expense_account or "Cost of Goods Sold - _TC", + "cost_center": args.cost_center or "_Test Cost Center - _TC", + "serial_no": args.serial_no, + "batch_no": args.batch_no, + }, + ) if not args.do_not_save: pos_inv.insert() @@ -662,7 +891,9 @@ def create_pos_invoice(**args): return pos_inv + def make_batch_item(item_name): from erpnext.stock.doctype.item.test_item import make_item + if not frappe.db.exists(item_name): - return make_item(item_name, dict(has_batch_no = 1, create_new_batch = 1, is_stock_item=1)) \ No newline at end of file + return make_item(item_name, dict(has_batch_no=1, create_new_batch=1, is_stock_item=1)) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index d4513c6a6863..d3a81fe61dc0 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -20,32 +20,44 @@ def validate(self): self.validate_pos_invoice_status() def validate_customer(self): - if self.merge_invoices_based_on == 'Customer Group': + if self.merge_invoices_based_on == "Customer Group": return for d in self.pos_invoices: if d.customer != self.customer: - frappe.throw(_("Row #{}: POS Invoice {} is not against customer {}").format(d.idx, d.pos_invoice, self.customer)) + frappe.throw( + _("Row #{}: POS Invoice {} is not against customer {}").format( + d.idx, d.pos_invoice, self.customer + ) + ) def validate_pos_invoice_status(self): for d in self.pos_invoices: status, docstatus, is_return, return_against = frappe.db.get_value( - 'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against']) + "POS Invoice", d.pos_invoice, ["status", "docstatus", "is_return", "return_against"] + ) 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)) if status == "Consolidated": - frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status)) - if is_return and return_against and return_against not in [d.pos_invoice for d in self.pos_invoices]: + frappe.throw( + _("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status) + ) + if ( + is_return + and return_against + and return_against not in [d.pos_invoice for d in self.pos_invoices] + ): bold_return_against = frappe.bold(return_against) - return_against_status = frappe.db.get_value('POS Invoice', return_against, "status") + return_against_status = frappe.db.get_value("POS Invoice", return_against, "status") if return_against_status != "Consolidated": # if return entry is not getting merged in the current pos closing and if it is not consolidated bold_unconsolidated = frappe.bold("not Consolidated") - msg = (_("Row #{}: Original Invoice {} of return invoice {} is {}.") - .format(d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated)) + msg = _("Row #{}: Original Invoice {} of return invoice {} is {}.").format( + 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 += "{message}
Thanks,
ERPNext Team. - """.format(gst_document_link=" ERPNext GST Document ") + """.format( + gst_document_link=" ERPNext GST Document " + ) try: sendmail_to_system_managers("[Important] ERPNext GST updates", message) diff --git a/erpnext/patches/v8_7/sync_india_custom_fields.py b/erpnext/patches/v8_7/sync_india_custom_fields.py index 808c833f6fcf..e1b9a732dea4 100644 --- a/erpnext/patches/v8_7/sync_india_custom_fields.py +++ b/erpnext/patches/v8_7/sync_india_custom_fields.py @@ -4,32 +4,39 @@ def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) + company = frappe.get_all("Company", filters={"country": "India"}) if not company: return - frappe.reload_doc('Payroll', 'doctype', 'payroll_period') - frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_declaration') - frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_proof_submission') - frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_declaration_category') - frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_proof_submission_detail') + frappe.reload_doc("Payroll", "doctype", "payroll_period") + frappe.reload_doc("Payroll", "doctype", "employee_tax_exemption_declaration") + frappe.reload_doc("Payroll", "doctype", "employee_tax_exemption_proof_submission") + frappe.reload_doc("Payroll", "doctype", "employee_tax_exemption_declaration_category") + frappe.reload_doc("Payroll", "doctype", "employee_tax_exemption_proof_submission_detail") - frappe.reload_doc('accounts', 'doctype', 'tax_category') + frappe.reload_doc("accounts", "doctype", "tax_category") for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]: - frappe.db.sql("""delete from `tabCustom Field` where dt = %s - and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", doctype) + frappe.db.sql( + """delete from `tabCustom Field` where dt = %s + and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", + doctype, + ) make_custom_fields() - frappe.db.sql(""" + frappe.db.sql( + """ update `tabCustom Field` set reqd = 0, `default` = '' where fieldname = 'reason_for_issuing_document' - """) + """ + ) - frappe.db.sql(""" + frappe.db.sql( + """ update tabAddress set gst_state_number=concat("0", gst_state_number) where ifnull(gst_state_number, '') != '' and gst_state_number<10 - """) + """ + ) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index d61856841683..18bd3b7733c0 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -30,12 +30,17 @@ def validate(self): frappe.throw(_("Amount should not be less than zero")) def validate_salary_structure(self): - if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): - frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): + frappe.throw( + _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( + self.employee + ) + ) def validate_recurring_additional_salary_overlap(self): if self.is_recurring: - additional_salaries = frappe.db.sql(""" + additional_salaries = frappe.db.sql( + """ SELECT name FROM `tabAdditional Salary` @@ -47,22 +52,28 @@ def validate_recurring_additional_salary_overlap(self): AND salary_component = %s AND to_date >= %s AND from_date <= %s""", - (self.employee, self.name, self.salary_component, self.from_date, self.to_date), as_dict = 1) + (self.employee, self.name, self.salary_component, self.from_date, self.to_date), + as_dict=1, + ) additional_salaries = [salary.name for salary in additional_salaries] if additional_salaries and len(additional_salaries): - frappe.throw(_("Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3}").format( - bold(comma_and(additional_salaries)), - bold(self.salary_component), - bold(formatdate(self.from_date)), - bold(formatdate(self.to_date) - ))) - + frappe.throw( + _( + "Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3}" + ).format( + bold(comma_and(additional_salaries)), + bold(self.salary_component), + bold(formatdate(self.from_date)), + bold(formatdate(self.to_date)), + ) + ) def validate_dates(self): - date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee, - ["date_of_joining", "relieving_date"]) + date_of_joining, relieving_date = frappe.db.get_value( + "Employee", self.employee, ["date_of_joining", "relieving_date"] + ) if getdate(self.from_date) > getdate(self.to_date): frappe.throw(_("From Date can not be greater than To Date.")) @@ -81,19 +92,27 @@ def validate_dates(self): def validate_employee_referral(self): if self.ref_doctype == "Employee Referral": - referral_details = frappe.db.get_value("Employee Referral", self.ref_docname, - ["is_applicable_for_referral_bonus", "status"], as_dict=1) + referral_details = frappe.db.get_value( + "Employee Referral", + self.ref_docname, + ["is_applicable_for_referral_bonus", "status"], + as_dict=1, + ) if not referral_details.is_applicable_for_referral_bonus: - frappe.throw(_("Employee Referral {0} is not applicable for referral bonus.").format( - self.ref_docname)) + frappe.throw( + _("Employee Referral {0} is not applicable for referral bonus.").format(self.ref_docname) + ) if self.type == "Deduction": frappe.throw(_("Earning Salary Component is required for Employee Referral Bonus.")) if referral_details.status != "Accepted": - frappe.throw(_("Additional Salary for referral bonus can only be created against Employee Referral with status {0}").format( - frappe.bold("Accepted"))) + frappe.throw( + _( + "Additional Salary for referral bonus can only be created against Employee Referral with status {0}" + ).format(frappe.bold("Accepted")) + ) def update_return_amount_in_employee_advance(self): if self.ref_doctype == "Employee Advance" and self.ref_docname: @@ -125,28 +144,54 @@ def get_amount(self, sal_start_date, sal_end_date): no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1 return amount_per_day * no_of_days + @frappe.whitelist() def get_additional_salaries(employee, start_date, end_date, component_type): - comp_type = 'Earning' if component_type == 'earnings' else 'Deduction' - - additional_sal = frappe.qb.DocType('Additional Salary') - component_field = additional_sal.salary_component.as_('component') - overwrite_field = additional_sal.overwrite_salary_structure_amount.as_('overwrite') - - additional_salary_list = frappe.qb.from_( - additional_sal - ).select( - additional_sal.name, component_field, additional_sal.type, - additional_sal.amount, additional_sal.is_recurring, overwrite_field, - additional_sal.deduct_full_tax_on_selected_payroll_date - ).where( - (additional_sal.employee == employee) - & (additional_sal.docstatus == 1) - & (additional_sal.type == comp_type) - ).where( - additional_sal.payroll_date[start_date: end_date] - | ((additional_sal.from_date <= end_date) & (additional_sal.to_date >= end_date)) - ).run(as_dict=True) + from frappe.query_builder import Criterion + + comp_type = "Earning" if component_type == "earnings" else "Deduction" + + additional_sal = frappe.qb.DocType("Additional Salary") + component_field = additional_sal.salary_component.as_("component") + overwrite_field = additional_sal.overwrite_salary_structure_amount.as_("overwrite") + + additional_salary_list = ( + frappe.qb.from_(additional_sal) + .select( + additional_sal.name, + component_field, + additional_sal.type, + additional_sal.amount, + additional_sal.is_recurring, + overwrite_field, + additional_sal.deduct_full_tax_on_selected_payroll_date, + ) + .where( + (additional_sal.employee == employee) + & (additional_sal.docstatus == 1) + & (additional_sal.type == comp_type) + ) + .where( + Criterion.any( + [ + Criterion.all( + [ # is recurring and additional salary dates fall within the payroll period + additional_sal.is_recurring == 1, + additional_sal.from_date <= end_date, + additional_sal.to_date >= end_date, + ] + ), + Criterion.all( + [ # is not recurring and additional salary's payroll date falls within the payroll period + additional_sal.is_recurring == 0, + additional_sal.payroll_date[start_date:end_date], + ] + ), + ] + ) + ) + .run(as_dict=True) + ) additional_salaries = [] components_to_overwrite = [] @@ -154,8 +199,12 @@ def get_additional_salaries(employee, start_date, end_date, component_type): for d in additional_salary_list: if d.overwrite: if d.component in components_to_overwrite: - frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}.").format( - frappe.bold(d.component), start_date, end_date), title=_("Error")) + frappe.throw( + _( + "Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}." + ).format(frappe.bold(d.component), start_date, end_date), + title=_("Error"), + ) components_to_overwrite.append(d.component) diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py index 84de912e431f..bd739368a0ac 100644 --- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py @@ -4,7 +4,8 @@ import unittest import frappe -from frappe.utils import add_days, nowdate +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, add_months, nowdate import erpnext from erpnext.hr.doctype.employee.test_employee import make_employee @@ -16,40 +17,87 @@ from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure -class TestAdditionalSalary(unittest.TestCase): - +class TestAdditionalSalary(FrappeTestCase): def setUp(self): setup_test() - def tearDown(self): - for dt in ["Salary Slip", "Additional Salary", "Salary Structure Assignment", "Salary Structure"]: - frappe.db.sql("delete from `tab%s`" % dt) - def test_recurring_additional_salary(self): amount = 0 salary_component = None emp_id = make_employee("test_additional@salary.com") frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800)) - salary_structure = make_salary_structure("Test Salary Structure Additional Salary", "Monthly", employee=emp_id) + salary_structure = make_salary_structure( + "Test Salary Structure Additional Salary", "Monthly", employee=emp_id + ) add_sal = get_additional_salary(emp_id) - ss = make_employee_salary_slip("test_additional@salary.com", "Monthly", salary_structure=salary_structure.name) + ss = make_employee_salary_slip( + "test_additional@salary.com", "Monthly", salary_structure=salary_structure.name + ) + for earning in ss.earnings: + if earning.salary_component == "Recurring Salary Component": + amount = earning.amount + salary_component = earning.salary_component + break + + self.assertEqual(amount, add_sal.amount) + self.assertEqual(salary_component, add_sal.salary_component) + + def test_non_recurring_additional_salary(self): + amount = 0 + salary_component = None + date = nowdate() + + emp_id = make_employee("test_additional@salary.com") + frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(date, 1800)) + salary_structure = make_salary_structure( + "Test Salary Structure Additional Salary", "Monthly", employee=emp_id + ) + add_sal = get_additional_salary(emp_id, recurring=False, payroll_date=date) + + ss = make_employee_salary_slip( + "test_additional@salary.com", "Monthly", salary_structure=salary_structure.name + ) + + amount, salary_component = None, None for earning in ss.earnings: if earning.salary_component == "Recurring Salary Component": amount = earning.amount salary_component = earning.salary_component + break self.assertEqual(amount, add_sal.amount) self.assertEqual(salary_component, add_sal.salary_component) -def get_additional_salary(emp_id): + # should not show up in next months + ss.posting_date = add_months(date, 1) + ss.start_date = ss.end_date = None + ss.earnings = [] + ss.deductions = [] + ss.save() + + amount, salary_component = None, None + for earning in ss.earnings: + if earning.salary_component == "Recurring Salary Component": + amount = earning.amount + salary_component = earning.salary_component + break + + self.assertIsNone(amount) + self.assertIsNone(salary_component) + + +def get_additional_salary(emp_id, recurring=True, payroll_date=None): create_salary_component("Recurring Salary Component") add_sal = frappe.new_doc("Additional Salary") add_sal.employee = emp_id add_sal.salary_component = "Recurring Salary Component" - add_sal.is_recurring = 1 + + add_sal.is_recurring = 1 if recurring else 0 add_sal.from_date = add_days(nowdate(), -50) add_sal.to_date = add_days(nowdate(), 180) + add_sal.payroll_date = payroll_date + add_sal.amount = 5000 add_sal.currency = erpnext.get_default_currency() add_sal.save() diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py index eda50150ebdb..0acd44711b07 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -34,19 +34,30 @@ def validate(self): if self.remaining_benefit > 0: self.validate_remaining_benefit_amount() else: - frappe.throw(_("As per your assigned Salary Structure you cannot apply for benefits").format(self.employee)) + frappe.throw( + _("As per your assigned Salary Structure you cannot apply for benefits").format(self.employee) + ) def validate_prev_benefit_claim(self): if self.employee_benefits: for benefit in self.employee_benefits: if benefit.pay_against_benefit_claim == 1: payroll_period = frappe.get_doc("Payroll Period", self.payroll_period) - benefit_claimed = get_previous_claimed_amount(self.employee, payroll_period, component = benefit.earning_component) - benefit_given = get_sal_slip_total_benefit_given(self.employee, payroll_period, component = benefit.earning_component) + benefit_claimed = get_previous_claimed_amount( + self.employee, payroll_period, component=benefit.earning_component + ) + benefit_given = get_sal_slip_total_benefit_given( + self.employee, payroll_period, component=benefit.earning_component + ) benefit_claim_remining = benefit_claimed - benefit_given if benefit_claimed > 0 and benefit_claim_remining > benefit.amount: - frappe.throw(_("An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}").format( - benefit_claimed, benefit.earning_component, benefit_claim_remining)) + frappe.throw( + _( + "An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}" + ).format( + benefit_claimed, benefit.earning_component, benefit_claim_remining + ) + ) def validate_remaining_benefit_amount(self): # check salary structure earnings have flexi component (sum of max_benefit_amount) @@ -65,20 +76,34 @@ def validate_remaining_benefit_amount(self): if salary_structure.earnings: for earnings in salary_structure.earnings: if earnings.is_flexible_benefit == 1 and earnings.salary_component not in benefit_components: - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", earnings.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( + "Salary Component", + earnings.salary_component, + ["pay_against_benefit_claim", "max_benefit_amount"], + ) if pay_against_benefit_claim != 1: pro_rata_amount += max_benefit_amount else: non_pro_rata_amount += max_benefit_amount - if pro_rata_amount == 0 and non_pro_rata_amount == 0: - frappe.throw(_("Please add the remaining benefits {0} to any of the existing component").format(self.remaining_benefit)) + if pro_rata_amount == 0 and non_pro_rata_amount == 0: + frappe.throw( + _("Please add the remaining benefits {0} to any of the existing component").format( + self.remaining_benefit + ) + ) elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit): - frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component").format( - non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount)) + frappe.throw( + _( + "You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component" + ).format(non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount) + ) elif non_pro_rata_amount == 0: - frappe.throw(_("Please add the remaining benefits {0} to the application as pro-rata component").format( - self.remaining_benefit)) + frappe.throw( + _("Please add the remaining benefits {0} to the application as pro-rata component").format( + self.remaining_benefit + ) + ) def validate_max_benefit_for_component(self): if self.employee_benefits: @@ -87,30 +112,43 @@ def validate_max_benefit_for_component(self): self.validate_max_benefit(employee_benefit.earning_component) max_benefit_amount += employee_benefit.amount if max_benefit_amount > self.max_benefits: - frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, self.max_benefits)) + frappe.throw( + _("Maximum benefit amount of employee {0} exceeds {1}").format( + self.employee, self.max_benefits + ) + ) def validate_max_benefit(self, earning_component_name): - max_benefit_amount = frappe.db.get_value("Salary Component", earning_component_name, "max_benefit_amount") + max_benefit_amount = frappe.db.get_value( + "Salary Component", earning_component_name, "max_benefit_amount" + ) benefit_amount = 0 for employee_benefit in self.employee_benefits: if employee_benefit.earning_component == earning_component_name: benefit_amount += employee_benefit.amount - prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given(self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name) + prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given( + self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name + ) benefit_amount += prev_sal_slip_flexi_amount if rounded(benefit_amount, 2) > max_benefit_amount: - frappe.throw(_("Maximum benefit amount of component {0} exceeds {1}").format(earning_component_name, max_benefit_amount)) + frappe.throw( + _("Maximum benefit amount of component {0} exceeds {1}").format( + earning_component_name, max_benefit_amount + ) + ) def validate_duplicate_on_payroll_period(self): application = frappe.db.exists( "Employee Benefit Application", - { - 'employee': self.employee, - 'payroll_period': self.payroll_period, - 'docstatus': 1 - } + {"employee": self.employee, "payroll_period": self.payroll_period, "docstatus": 1}, ) if application: - frappe.throw(_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(self.employee, application, self.payroll_period)) + frappe.throw( + _("Employee {0} already submited an apllication {1} for the payroll period {2}").format( + self.employee, application, self.payroll_period + ) + ) + @frappe.whitelist() def get_max_benefits(employee, on_date): @@ -121,6 +159,7 @@ def get_max_benefits(employee, on_date): return max_benefits return False + @frappe.whitelist() def get_max_benefits_remaining(employee, on_date, payroll_period): max_benefits = get_max_benefits(employee, on_date) @@ -141,9 +180,14 @@ def get_max_benefits_remaining(employee, on_date, payroll_period): sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) for sal_struct_row in sal_struct.get("earnings"): salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component) - if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1: + if ( + salary_component.depends_on_payment_days == 1 + and salary_component.pay_against_benefit_claim != 1 + ): have_depends_on_payment_days = True - benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, salary_component.max_benefit_amount) + benefit_amount = get_benefit_amount_based_on_pro_rata( + sal_struct, salary_component.max_benefit_amount + ) amount_per_day = benefit_amount / payroll_period_days per_day_amount_total += amount_per_day @@ -159,12 +203,14 @@ def get_max_benefits_remaining(employee, on_date, payroll_period): return max_benefits - prev_sal_slip_flexi_total return max_benefits + def calculate_lwp(employee, start_date, holidays, working_days): lwp = 0 holidays = "','".join(holidays) for d in range(working_days): dt = add_days(cstr(getdate(start_date)), d) - leave = frappe.db.sql(""" + leave = frappe.db.sql( + """ select t1.name, t1.half_day from `tabLeave Application` t1, `tabLeave Type` t2 where t2.name = t1.leave_type @@ -174,56 +220,77 @@ def calculate_lwp(employee, start_date, holidays, working_days): and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date WHEN t2.include_holiday THEN %(dt)s between from_date and to_date END - """.format(holidays), {"employee": employee, "dt": dt}) + """.format( + holidays + ), + {"employee": employee, "dt": dt}, + ) if leave: lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1) return lwp -def get_benefit_component_amount(employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period): + +def get_benefit_component_amount( + employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period +): if not payroll_period: - frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}") - .format(salary_component)) + frappe.msgprint( + _("Start and end dates not in a valid Payroll Period, cannot calculate {0}").format( + salary_component + ) + ) return False # Considering there is only one application for a year - benefit_application = frappe.db.sql(""" + benefit_application = frappe.db.sql( + """ select name from `tabEmployee Benefit Application` where payroll_period=%(payroll_period)s and employee=%(employee)s and docstatus = 1 - """, { - 'employee': employee, - 'payroll_period': payroll_period.name - }) + """, + {"employee": employee, "payroll_period": payroll_period.name}, + ) current_benefit_amount = 0.0 - component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component", - salary_component, ["max_benefit_amount", "depends_on_payment_days"]) + component_max_benefit, depends_on_payment_days = frappe.db.get_value( + "Salary Component", salary_component, ["max_benefit_amount", "depends_on_payment_days"] + ) benefit_amount = 0 if benefit_application: - benefit_amount = frappe.db.get_value("Employee Benefit Application Detail", - {"parent": benefit_application[0][0], "earning_component": salary_component}, "amount") + benefit_amount = frappe.db.get_value( + "Employee Benefit Application Detail", + {"parent": benefit_application[0][0], "earning_component": salary_component}, + "amount", + ) elif component_max_benefit: benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit) current_benefit_amount = 0 if benefit_amount: - total_sub_periods = get_period_factor(employee, - start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days)[0] + total_sub_periods = get_period_factor( + employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days + )[0] current_benefit_amount = benefit_amount / total_sub_periods return current_benefit_amount + def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit): max_benefits_total = 0 benefit_amount = 0 for d in sal_struct.get("earnings"): if d.is_flexible_benefit == 1: - component = frappe.db.get_value("Salary Component", d.salary_component, ["max_benefit_amount", "pay_against_benefit_claim"], as_dict=1) + component = frappe.db.get_value( + "Salary Component", + d.salary_component, + ["max_benefit_amount", "pay_against_benefit_claim"], + as_dict=1, + ) if not component.pay_against_benefit_claim: max_benefits_total += component.max_benefit_amount @@ -234,34 +301,46 @@ def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit): return benefit_amount + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_earning_components(doctype, txt, searchfield, start, page_len, filters): if len(filters) < 2: return {} - salary_structure = get_assigned_salary_structure(filters['employee'], filters['date']) + salary_structure = get_assigned_salary_structure(filters["employee"], filters["date"]) if salary_structure: - return frappe.db.sql(""" + return frappe.db.sql( + """ select salary_component from `tabSalary Detail` where parent = %s and is_flexible_benefit = 1 order by name - """, salary_structure) + """, + salary_structure, + ) else: - frappe.throw(_("Salary Structure not found for employee {0} and date {1}") - .format(filters['employee'], filters['date'])) + frappe.throw( + _("Salary Structure not found for employee {0} and date {1}").format( + filters["employee"], filters["date"] + ) + ) + @frappe.whitelist() def get_earning_components_max_benefits(employee, date, earning_component): salary_structure = get_assigned_salary_structure(employee, date) - amount = frappe.db.sql(""" + amount = frappe.db.sql( + """ select amount from `tabSalary Detail` where parent = %s and is_flexible_benefit = 1 and salary_component = %s order by name - """, salary_structure, earning_component) + """, + salary_structure, + earning_component, + ) return amount if amount else 0 diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py index 801ce4ba3679..31f26b25e73e 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -23,9 +23,15 @@ def validate(self): max_benefits = get_max_benefits(self.employee, self.claim_date) if not max_benefits or max_benefits <= 0: frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) - payroll_period = get_payroll_period(self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company")) + payroll_period = get_payroll_period( + self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company") + ) if not payroll_period: - frappe.throw(_("{0} is not in a valid Payroll Period").format(frappe.format(self.claim_date, dict(fieldtype='Date')))) + frappe.throw( + _("{0} is not in a valid Payroll Period").format( + frappe.format(self.claim_date, dict(fieldtype="Date")) + ) + ) self.validate_max_benefit_for_component(payroll_period) self.validate_max_benefit_for_sal_struct(max_benefits) self.validate_benefit_claim_amount(max_benefits, payroll_period) @@ -36,21 +42,31 @@ def validate_benefit_claim_amount(self, max_benefits, payroll_period): claimed_amount = self.claimed_amount claimed_amount += get_previous_claimed_amount(self.employee, payroll_period) if max_benefits < claimed_amount: - frappe.throw(_("Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed\ - amount").format(self.employee, max_benefits, claimed_amount-max_benefits)) + frappe.throw( + _( + "Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed\ + amount" + ).format(self.employee, max_benefits, claimed_amount - max_benefits) + ) def validate_max_benefit_for_sal_struct(self, max_benefits): if self.claimed_amount > max_benefits: - frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits)) + frappe.throw( + _("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits) + ) def validate_max_benefit_for_component(self, payroll_period): if self.max_amount_eligible: claimed_amount = self.claimed_amount - claimed_amount += get_previous_claimed_amount(self.employee, - payroll_period, component = self.earning_component) + claimed_amount += get_previous_claimed_amount( + self.employee, payroll_period, component=self.earning_component + ) if claimed_amount > self.max_amount_eligible: - frappe.throw(_("Maximum amount eligible for the component {0} exceeds {1}") - .format(self.earning_component, self.max_amount_eligible)) + frappe.throw( + _("Maximum amount eligible for the component {0} exceeds {1}").format( + self.earning_component, self.max_amount_eligible + ) + ) def validate_non_pro_rata_benefit_claim(self, max_benefits, payroll_period): claimed_amount = self.claimed_amount @@ -64,30 +80,39 @@ def validate_non_pro_rata_benefit_claim(self, max_benefits, payroll_period): sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) pro_rata_amount = get_benefit_pro_rata_ratio_amount(self.employee, self.claim_date, sal_struct) - claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata = True) + claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata=True) if max_benefits < pro_rata_amount + claimed_amount: - frappe.throw(_("Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component\ - amount and previous claimed amount").format(self.employee, max_benefits, pro_rata_amount+claimed_amount-max_benefits)) + frappe.throw( + _( + "Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component\ + amount and previous claimed amount" + ).format( + self.employee, max_benefits, pro_rata_amount + claimed_amount - max_benefits + ) + ) def get_pro_rata_amount_in_application(self, payroll_period): application = frappe.db.exists( "Employee Benefit Application", - { - 'employee': self.employee, - 'payroll_period': payroll_period, - 'docstatus': 1 - } + {"employee": self.employee, "payroll_period": payroll_period, "docstatus": 1}, ) if application: - return frappe.db.get_value("Employee Benefit Application", application, "pro_rata_dispensed_amount") + return frappe.db.get_value( + "Employee Benefit Application", application, "pro_rata_dispensed_amount" + ) return False + def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): total_pro_rata_max = 0 benefit_amount_total = 0 for sal_struct_row in sal_struct.get("earnings"): try: - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( + "Salary Component", + sal_struct_row.salary_component, + ["pay_against_benefit_claim", "max_benefit_amount"], + ) except TypeError: # show the error in tests? frappe.throw(_("Unable to find Salary Component {0}").format(sal_struct_row.salary_component)) @@ -95,7 +120,11 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): total_pro_rata_max += max_benefit_amount if total_pro_rata_max > 0: for sal_struct_row in sal_struct.get("earnings"): - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( + "Salary Component", + sal_struct_row.salary_component, + ["pay_against_benefit_claim", "max_benefit_amount"], + ) if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: component_max = max_benefit_amount @@ -105,6 +134,7 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): benefit_amount_total += benefit_amount return benefit_amount_total + def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None): query = """ select sum(claimed_amount) @@ -119,41 +149,54 @@ def get_benefit_claim_amount(employee, start_date, end_date, salary_component=No if salary_component: query += " and earning_component = %(earning_component)s" - claimed_amount = flt(frappe.db.sql(query, { - 'employee': employee, - 'start_date': start_date, - 'end_date': end_date, - 'earning_component': salary_component - })[0][0]) + claimed_amount = flt( + frappe.db.sql( + query, + { + "employee": employee, + "start_date": start_date, + "end_date": end_date, + "earning_component": salary_component, + }, + )[0][0] + ) return claimed_amount + def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period): pro_rata_amount = 0 claimed_amount = 0 application = frappe.db.exists( "Employee Benefit Application", - { - 'employee': employee, - 'payroll_period': payroll_period.name, - 'docstatus': 1 - } + {"employee": employee, "payroll_period": payroll_period.name, "docstatus": 1}, ) if application: application_obj = frappe.get_doc("Employee Benefit Application", application) - pro_rata_amount = application_obj.pro_rata_dispensed_amount + application_obj.max_benefits - application_obj.remaining_benefit + pro_rata_amount = ( + application_obj.pro_rata_dispensed_amount + + application_obj.max_benefits + - application_obj.remaining_benefit + ) else: pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct) - claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date) + claimed_amount += get_benefit_claim_amount( + employee, payroll_period.start_date, payroll_period.end_date + ) return claimed_amount + pro_rata_amount -def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct): + +def get_last_payroll_period_benefits( + employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct +): max_benefits = get_max_benefits(employee, payroll_period.end_date) if not max_benefits: max_benefits = 0 - remaining_benefit = max_benefits - get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period) + remaining_benefit = max_benefits - get_total_benefit_dispensed( + employee, sal_struct, sal_slip_start_date, payroll_period + ) if remaining_benefit > 0: have_remaining = True # Set the remaining benefits to flexi non pro-rata component in the salary structure @@ -162,7 +205,9 @@ def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end if d.is_flexible_benefit == 1: salary_component = frappe.get_doc("Salary Component", d.salary_component) if salary_component.pay_against_benefit_claim == 1: - claimed_amount = get_benefit_claim_amount(employee, payroll_period.start_date, sal_slip_end_date, d.salary_component) + claimed_amount = get_benefit_claim_amount( + employee, payroll_period.start_date, sal_slip_end_date, d.salary_component + ) amount_fit_to_component = salary_component.max_benefit_amount - claimed_amount if amount_fit_to_component > 0: if remaining_benefit > amount_fit_to_component: @@ -171,19 +216,23 @@ def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end else: amount = remaining_benefit have_remaining = False - current_claimed_amount = get_benefit_claim_amount(employee, sal_slip_start_date, sal_slip_end_date, d.salary_component) + current_claimed_amount = get_benefit_claim_amount( + employee, sal_slip_start_date, sal_slip_end_date, d.salary_component + ) amount += current_claimed_amount struct_row = {} salary_components_dict = {} - struct_row['depends_on_payment_days'] = salary_component.depends_on_payment_days - struct_row['salary_component'] = salary_component.name - struct_row['abbr'] = salary_component.salary_component_abbr - struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total - struct_row['is_tax_applicable'] = salary_component.is_tax_applicable, - struct_row['is_flexible_benefit'] = salary_component.is_flexible_benefit, - struct_row['variable_based_on_taxable_salary'] = salary_component.variable_based_on_taxable_salary - salary_components_dict['amount'] = amount - salary_components_dict['struct_row'] = struct_row + struct_row["depends_on_payment_days"] = salary_component.depends_on_payment_days + struct_row["salary_component"] = salary_component.name + struct_row["abbr"] = salary_component.salary_component_abbr + struct_row["do_not_include_in_total"] = salary_component.do_not_include_in_total + struct_row["is_tax_applicable"] = (salary_component.is_tax_applicable,) + struct_row["is_flexible_benefit"] = (salary_component.is_flexible_benefit,) + struct_row[ + "variable_based_on_taxable_salary" + ] = salary_component.variable_based_on_taxable_salary + salary_components_dict["amount"] = amount + salary_components_dict["struct_row"] = struct_row salary_components_array.append(salary_components_dict) if not have_remaining: break diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py index a37e22425f72..7686185349f1 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py @@ -15,13 +15,17 @@ def validate(self): self.validate_salary_structure() def validate_salary_structure(self): - if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): - frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): + frappe.throw( + _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( + self.employee + ) + ) def on_submit(self): - company = frappe.db.get_value('Employee', self.employee, 'company') + company = frappe.db.get_value("Employee", self.employee, "company") - additional_salary = frappe.new_doc('Additional Salary') + additional_salary = frappe.new_doc("Additional Salary") additional_salary.employee = self.employee additional_salary.currency = self.currency additional_salary.salary_component = self.salary_component diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index 9b5eab636f15..c0ef2eee78c6 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -20,7 +20,9 @@ class EmployeeTaxExemptionDeclaration(Document): def validate(self): validate_active_employee(self.employee) validate_tax_declaration(self.declarations) - validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) + validate_duplicate_exemption_for_payroll_period( + self.doctype, self.name, self.payroll_period, self.employee + ) self.set_total_declared_amount() self.set_total_exemption_amount() self.calculate_hra_exemption() @@ -43,17 +45,23 @@ def calculate_hra_exemption(self): self.annual_hra_exemption = hra_exemption["annual_exemption"] self.monthly_hra_exemption = hra_exemption["monthly_exemption"] + @frappe.whitelist() def make_proof_submission(source_name, target_doc=None): - doclist = get_mapped_doc("Employee Tax Exemption Declaration", source_name, { - "Employee Tax Exemption Declaration": { - "doctype": "Employee Tax Exemption Proof Submission", - "field_no_map": ["monthly_house_rent", "monthly_hra_exemption"] + doclist = get_mapped_doc( + "Employee Tax Exemption Declaration", + source_name, + { + "Employee Tax Exemption Declaration": { + "doctype": "Employee Tax Exemption Proof Submission", + "field_no_map": ["monthly_house_rent", "monthly_hra_exemption"], + }, + "Employee Tax Exemption Declaration Category": { + "doctype": "Employee Tax Exemption Proof Submission Detail", + "add_if_empty": True, + }, }, - "Employee Tax Exemption Declaration Category": { - "doctype": "Employee Tax Exemption Proof Submission Detail", - "add_if_empty": True - } - }, target_doc) + target_doc, + ) return doclist diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index fc28afdc3e51..1d90e7383fef 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -19,112 +19,147 @@ def setUp(self): frappe.db.sql("""delete from `tabEmployee Tax Exemption Declaration`""") def test_duplicate_category_in_declaration(self): - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000), - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 50000) - ] - }) + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=100000, + ), + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=50000, + ), + ], + } + ) self.assertRaises(frappe.ValidationError, declaration.save) def test_duplicate_entry_for_payroll_period(self): - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000), - dict(exemption_sub_category = "_Test1 Sub Category", - exemption_category = "_Test Category", - amount = 50000), - ] - }).insert() + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=100000, + ), + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=50000, + ), + ], + } + ).insert() - duplicate_declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 100000) - ] - }) + duplicate_declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=100000, + ) + ], + } + ) self.assertRaises(DuplicateDeclarationError, duplicate_declaration.insert) - duplicate_declaration.employee = frappe.get_value("Employee", {"user_id":"employee1@taxexepmtion.com"}, "name") + duplicate_declaration.employee = frappe.get_value( + "Employee", {"user_id": "employee1@taxexepmtion.com"}, "name" + ) self.assertTrue(duplicate_declaration.insert) def test_exemption_amount(self): - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 80000), - dict(exemption_sub_category = "_Test1 Sub Category", - exemption_category = "_Test Category", - amount = 60000), - ] - }).insert() + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Declaration", + "employee": frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name"), + "company": erpnext.get_default_company(), + "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), + "declarations": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=80000, + ), + dict( + exemption_sub_category="_Test1 Sub Category", + exemption_category="_Test Category", + amount=60000, + ), + ], + } + ).insert() self.assertEqual(declaration.total_exemption_amount, 100000) + def create_payroll_period(**args): args = frappe._dict(args) name = args.name or "_Test Payroll Period" if not frappe.db.exists("Payroll Period", name): from datetime import date - payroll_period = frappe.get_doc(dict( - doctype = 'Payroll Period', - name = name, - company = args.company or erpnext.get_default_company(), - start_date = args.start_date or date(date.today().year, 1, 1), - end_date = args.end_date or date(date.today().year, 12, 31) - )).insert() + + payroll_period = frappe.get_doc( + dict( + doctype="Payroll Period", + name=name, + company=args.company or erpnext.get_default_company(), + start_date=args.start_date or date(date.today().year, 1, 1), + end_date=args.end_date or date(date.today().year, 12, 31), + ) + ).insert() return payroll_period else: return frappe.get_doc("Payroll Period", name) + def create_exemption_category(): if not frappe.db.exists("Employee Tax Exemption Category", "_Test Category"): - category = frappe.get_doc({ - "doctype": "Employee Tax Exemption Category", - "name": "_Test Category", - "deduction_component": "Income Tax", - "max_amount": 100000 - }).insert() + category = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Category", + "name": "_Test Category", + "deduction_component": "Income Tax", + "max_amount": 100000, + } + ).insert() if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Sub Category"): - frappe.get_doc({ - "doctype": "Employee Tax Exemption Sub Category", - "name": "_Test Sub Category", - "exemption_category": "_Test Category", - "max_amount": 100000, - "is_active": 1 - }).insert() + frappe.get_doc( + { + "doctype": "Employee Tax Exemption Sub Category", + "name": "_Test Sub Category", + "exemption_category": "_Test Category", + "max_amount": 100000, + "is_active": 1, + } + ).insert() if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test1 Sub Category"): - frappe.get_doc({ - "doctype": "Employee Tax Exemption Sub Category", - "name": "_Test1 Sub Category", - "exemption_category": "_Test Category", - "max_amount": 50000, - "is_active": 1 - }).insert() + frappe.get_doc( + { + "doctype": "Employee Tax Exemption Sub Category", + "name": "_Test1 Sub Category", + "exemption_category": "_Test Category", + "max_amount": 50000, + "is_active": 1, + } + ).insert() diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 56e73b37dff7..c52efaba5924 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -21,7 +21,9 @@ def validate(self): self.set_total_actual_amount() self.set_total_exemption_amount() self.calculate_hra_exemption() - validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) + validate_duplicate_exemption_for_payroll_period( + self.doctype, self.name, self.payroll_period, self.employee + ) def set_total_actual_amount(self): self.total_actual_amount = flt(self.get("house_rent_payment_amount")) diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py index f2aa64c28789..58b2c1af0588 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py @@ -19,40 +19,59 @@ def setup(self): frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission`""") def test_exemption_amount_lesser_than_category_max(self): - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Proof Submission", - "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), - "payroll_period": "Test Payroll Period", - "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", - type_of_proof = "Test Proof", - exemption_category = "_Test Category", - amount = 150000)] - }) + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [ + dict( + exemption_sub_category="_Test Sub Category", + type_of_proof="Test Proof", + exemption_category="_Test Category", + amount=150000, + ) + ], + } + ) self.assertRaises(frappe.ValidationError, declaration.save) - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Proof Submission", - "payroll_period": "Test Payroll Period", - "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), - "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", - type_of_proof = "Test Proof", - exemption_category = "_Test Category", - amount = 100000)] - }) + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "payroll_period": "Test Payroll Period", + "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), + "tax_exemption_proofs": [ + dict( + exemption_sub_category="_Test Sub Category", + type_of_proof="Test Proof", + exemption_category="_Test Category", + amount=100000, + ) + ], + } + ) self.assertTrue(declaration.save) self.assertTrue(declaration.submit) def test_duplicate_category_in_proof_submission(self): - declaration = frappe.get_doc({ - "doctype": "Employee Tax Exemption Proof Submission", - "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), - "payroll_period": "Test Payroll Period", - "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - type_of_proof = "Test Proof", - amount = 100000), - dict(exemption_sub_category = "_Test Sub Category", - exemption_category = "_Test Category", - amount = 50000), - ] - }) + declaration = frappe.get_doc( + { + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [ + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + type_of_proof="Test Proof", + amount=100000, + ), + dict( + exemption_sub_category="_Test Sub Category", + exemption_category="_Test Category", + amount=50000, + ), + ], + } + ) self.assertRaises(frappe.ValidationError, declaration.save) diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py index 4ac11f7112df..fb75d6706c33 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py @@ -10,7 +10,12 @@ class EmployeeTaxExemptionSubCategory(Document): def validate(self): - category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", self.exemption_category, "max_amount") + category_max_amount = frappe.db.get_value( + "Employee Tax Exemption Category", self.exemption_category, "max_amount" + ) if flt(self.max_amount) > flt(category_max_amount): - frappe.throw(_("Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}") - .format(category_max_amount, self.exemption_category)) + frappe.throw( + _( + "Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}" + ).format(category_max_amount, self.exemption_category) + ) diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py index 939634a9310e..91740ae8c6c6 100644 --- a/erpnext/payroll/doctype/gratuity/gratuity.py +++ b/erpnext/payroll/doctype/gratuity/gratuity.py @@ -27,7 +27,7 @@ def on_submit(self): self.create_gl_entries() def on_cancel(self): - self.ignore_linked_doctypes = ['GL Entry'] + self.ignore_linked_doctypes = ["GL Entry"] self.create_gl_entries(cancel=True) def create_gl_entries(self, cancel=False): @@ -39,28 +39,34 @@ def get_gl_entries(self): # payable entry if self.amount: gl_entry.append( - self.get_gl_dict({ - "account": self.payable_account, - "credit": self.amount, - "credit_in_account_currency": self.amount, - "against": self.expense_account, - "party_type": "Employee", - "party": self.employee, - "against_voucher_type": self.doctype, - "against_voucher": self.name, - "cost_center": self.cost_center - }, item=self) + self.get_gl_dict( + { + "account": self.payable_account, + "credit": self.amount, + "credit_in_account_currency": self.amount, + "against": self.expense_account, + "party_type": "Employee", + "party": self.employee, + "against_voucher_type": self.doctype, + "against_voucher": self.name, + "cost_center": self.cost_center, + }, + item=self, + ) ) # expense entries gl_entry.append( - self.get_gl_dict({ - "account": self.expense_account, - "debit": self.amount, - "debit_in_account_currency": self.amount, - "against": self.payable_account, - "cost_center": self.cost_center - }, item=self) + self.get_gl_dict( + { + "account": self.expense_account, + "debit": self.amount, + "debit_in_account_currency": self.amount, + "against": self.payable_account, + "cost_center": self.cost_center, + }, + item=self, + ) ) else: frappe.throw(_("Total Amount can not be zero")) @@ -69,7 +75,7 @@ def get_gl_entries(self): def create_additional_salary(self): if self.pay_via_salary_slip: - additional_salary = frappe.new_doc('Additional Salary') + additional_salary = frappe.new_doc("Additional Salary") additional_salary.employee = self.employee additional_salary.salary_component = self.salary_component additional_salary.overwrite_salary_structure_amount = 0 @@ -81,19 +87,22 @@ def create_additional_salary(self): additional_salary.submit() def set_total_advance_paid(self): - paid_amount = frappe.db.sql(""" + paid_amount = frappe.db.sql( + """ select ifnull(sum(debit_in_account_currency), 0) as paid_amount from `tabGL Entry` where against_voucher_type = 'Gratuity' and against_voucher = %s and party_type = 'Employee' and party = %s - """, (self.name, self.employee), as_dict=1)[0].paid_amount + """, + (self.name, self.employee), + as_dict=1, + )[0].paid_amount if flt(paid_amount) > self.amount: frappe.throw(_("Row {0}# Paid Amount cannot be greater than Total amount")) - self.db_set("paid_amount", paid_amount) if self.amount == self.paid_amount: self.db_set("status", "Paid") @@ -104,69 +113,97 @@ def calculate_work_experience_and_amount(employee, gratuity_rule): current_work_experience = calculate_work_experience(employee, gratuity_rule) or 0 gratuity_amount = calculate_gratuity_amount(employee, gratuity_rule, current_work_experience) or 0 - return {'current_work_experience': current_work_experience, "amount": gratuity_amount} + return {"current_work_experience": current_work_experience, "amount": gratuity_amount} + def calculate_work_experience(employee, gratuity_rule): - total_working_days_per_year, minimum_year_for_gratuity = frappe.db.get_value("Gratuity Rule", gratuity_rule, ["total_working_days_per_year", "minimum_year_for_gratuity"]) + total_working_days_per_year, minimum_year_for_gratuity = frappe.db.get_value( + "Gratuity Rule", gratuity_rule, ["total_working_days_per_year", "minimum_year_for_gratuity"] + ) - date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date']) + date_of_joining, relieving_date = frappe.db.get_value( + "Employee", employee, ["date_of_joining", "relieving_date"] + ) if not relieving_date: - frappe.throw(_("Please set Relieving Date for employee: {0}").format(bold(get_link_to_form("Employee", employee)))) + frappe.throw( + _("Please set Relieving Date for employee: {0}").format( + bold(get_link_to_form("Employee", employee)) + ) + ) - method = frappe.db.get_value("Gratuity Rule", gratuity_rule, "work_experience_calculation_function") - employee_total_workings_days = calculate_employee_total_workings_days(employee, date_of_joining, relieving_date) + method = frappe.db.get_value( + "Gratuity Rule", gratuity_rule, "work_experience_calculation_function" + ) + employee_total_workings_days = calculate_employee_total_workings_days( + employee, date_of_joining, relieving_date + ) - current_work_experience = employee_total_workings_days/total_working_days_per_year or 1 - current_work_experience = get_work_experience_using_method(method, current_work_experience, minimum_year_for_gratuity, employee) + current_work_experience = employee_total_workings_days / total_working_days_per_year or 1 + current_work_experience = get_work_experience_using_method( + method, current_work_experience, minimum_year_for_gratuity, employee + ) return current_work_experience -def calculate_employee_total_workings_days(employee, date_of_joining, relieving_date ): + +def calculate_employee_total_workings_days(employee, date_of_joining, relieving_date): employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") or "Leave" if payroll_based_on == "Leave": total_lwp = get_non_working_days(employee, relieving_date, "On Leave") employee_total_workings_days -= total_lwp - elif payroll_based_on == "Attendance": + elif payroll_based_on == "Attendance": total_absents = get_non_working_days(employee, relieving_date, "Absent") employee_total_workings_days -= total_absents return employee_total_workings_days -def get_work_experience_using_method(method, current_work_experience, minimum_year_for_gratuity, employee): + +def get_work_experience_using_method( + method, current_work_experience, minimum_year_for_gratuity, employee +): if method == "Round off Work Experience": current_work_experience = round(current_work_experience) else: current_work_experience = floor(current_work_experience) if current_work_experience < minimum_year_for_gratuity: - frappe.throw(_("Employee: {0} have to complete minimum {1} years for gratuity").format(bold(employee), minimum_year_for_gratuity)) + frappe.throw( + _("Employee: {0} have to complete minimum {1} years for gratuity").format( + bold(employee), minimum_year_for_gratuity + ) + ) return current_work_experience + def get_non_working_days(employee, relieving_date, status): - filters={ - "docstatus": 1, - "status": status, - "employee": employee, - "attendance_date": ("<=", get_datetime(relieving_date)) - } + filters = { + "docstatus": 1, + "status": status, + "employee": employee, + "attendance_date": ("<=", get_datetime(relieving_date)), + } if status == "On Leave": - lwp_leave_types = frappe.get_list("Leave Type", filters = {"is_lwp":1}) + lwp_leave_types = frappe.get_list("Leave Type", filters={"is_lwp": 1}) lwp_leave_types = [leave_type.name for leave_type in lwp_leave_types] - filters["leave_type"] = ("IN", lwp_leave_types) + filters["leave_type"] = ("IN", lwp_leave_types) - - record = frappe.get_all("Attendance", filters=filters, fields = ["COUNT(name) as total_lwp"]) + record = frappe.get_all("Attendance", filters=filters, fields=["COUNT(name) as total_lwp"]) return record[0].total_lwp if len(record) else 0 + def calculate_gratuity_amount(employee, gratuity_rule, experience): applicable_earnings_component = get_applicable_components(gratuity_rule) - total_applicable_components_amount = get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule) + total_applicable_components_amount = get_total_applicable_component_amount( + employee, applicable_earnings_component, gratuity_rule + ) - calculate_gratuity_amount_based_on = frappe.db.get_value("Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on") + calculate_gratuity_amount_based_on = frappe.db.get_value( + "Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on" + ) gratuity_amount = 0 slabs = get_gratuity_rule_slabs(gratuity_rule) slab_found = False @@ -174,49 +211,78 @@ def calculate_gratuity_amount(employee, gratuity_rule, experience): for slab in slabs: if calculate_gratuity_amount_based_on == "Current Slab": - slab_found, gratuity_amount = calculate_amount_based_on_current_slab(slab.from_year, slab.to_year, - experience, total_applicable_components_amount, slab.fraction_of_applicable_earnings) + slab_found, gratuity_amount = calculate_amount_based_on_current_slab( + slab.from_year, + slab.to_year, + experience, + total_applicable_components_amount, + slab.fraction_of_applicable_earnings, + ) if slab_found: - break + break elif calculate_gratuity_amount_based_on == "Sum of all previous slabs": if slab.to_year == 0 and slab.from_year == 0: - gratuity_amount += year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings + gratuity_amount += ( + year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings + ) slab_found = True break - if experience > slab.to_year and experience > slab.from_year and slab.to_year !=0: - gratuity_amount += (slab.to_year - slab.from_year) * total_applicable_components_amount * slab.fraction_of_applicable_earnings - year_left -= (slab.to_year - slab.from_year) + if experience > slab.to_year and experience > slab.from_year and slab.to_year != 0: + gratuity_amount += ( + (slab.to_year - slab.from_year) + * total_applicable_components_amount + * slab.fraction_of_applicable_earnings + ) + year_left -= slab.to_year - slab.from_year slab_found = True elif slab.from_year <= experience and (experience < slab.to_year or slab.to_year == 0): - gratuity_amount += year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings + gratuity_amount += ( + year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings + ) slab_found = True if not slab_found: - frappe.throw(_("No Suitable Slab found for Calculation of gratuity amount in Gratuity Rule: {0}").format(bold(gratuity_rule))) + frappe.throw( + _("No Suitable Slab found for Calculation of gratuity amount in Gratuity Rule: {0}").format( + bold(gratuity_rule) + ) + ) return gratuity_amount + def get_applicable_components(gratuity_rule): - applicable_earnings_component = frappe.get_all("Gratuity Applicable Component", filters= {'parent': gratuity_rule}, fields=["salary_component"]) + applicable_earnings_component = frappe.get_all( + "Gratuity Applicable Component", filters={"parent": gratuity_rule}, fields=["salary_component"] + ) if len(applicable_earnings_component) == 0: - frappe.throw(_("No Applicable Earnings Component found for Gratuity Rule: {0}").format(bold(get_link_to_form("Gratuity Rule",gratuity_rule)))) - applicable_earnings_component = [component.salary_component for component in applicable_earnings_component] + frappe.throw( + _("No Applicable Earnings Component found for Gratuity Rule: {0}").format( + bold(get_link_to_form("Gratuity Rule", gratuity_rule)) + ) + ) + applicable_earnings_component = [ + component.salary_component for component in applicable_earnings_component + ] return applicable_earnings_component + def get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule): - sal_slip = get_last_salary_slip(employee) + sal_slip = get_last_salary_slip(employee) if not sal_slip: frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee))) - component_and_amounts = frappe.get_all("Salary Detail", + component_and_amounts = frappe.get_all( + "Salary Detail", filters={ "docstatus": 1, - 'parent': sal_slip, + "parent": sal_slip, "parentfield": "earnings", - 'salary_component': ('in', applicable_earnings_component) + "salary_component": ("in", applicable_earnings_component), }, - fields=["amount"]) + fields=["amount"], + ) total_applicable_components_amount = 0 if not len(component_and_amounts): frappe.throw(_("No Applicable Component is present in last month salary slip")) @@ -224,30 +290,44 @@ def get_total_applicable_component_amount(employee, applicable_earnings_componen total_applicable_components_amount += data.amount return total_applicable_components_amount -def calculate_amount_based_on_current_slab(from_year, to_year, experience, total_applicable_components_amount, fraction_of_applicable_earnings): - slab_found = False; gratuity_amount = 0 + +def calculate_amount_based_on_current_slab( + from_year, + to_year, + experience, + total_applicable_components_amount, + fraction_of_applicable_earnings, +): + slab_found = False + gratuity_amount = 0 if experience >= from_year and (to_year == 0 or experience < to_year): - gratuity_amount = total_applicable_components_amount * experience * fraction_of_applicable_earnings + gratuity_amount = ( + total_applicable_components_amount * experience * fraction_of_applicable_earnings + ) if fraction_of_applicable_earnings: slab_found = True return slab_found, gratuity_amount + def get_gratuity_rule_slabs(gratuity_rule): - return frappe.get_all("Gratuity Rule Slab", filters= {'parent': gratuity_rule}, fields = ["*"], order_by="idx") + return frappe.get_all( + "Gratuity Rule Slab", filters={"parent": gratuity_rule}, fields=["*"], order_by="idx" + ) + def get_salary_structure(employee): - return frappe.get_list("Salary Structure Assignment", filters = { - "employee": employee, 'docstatus': 1 - }, + return frappe.get_list( + "Salary Structure Assignment", + filters={"employee": employee, "docstatus": 1}, fields=["from_date", "salary_structure"], - order_by = "from_date desc")[0].salary_structure + order_by="from_date desc", + )[0].salary_structure + def get_last_salary_slip(employee): - salary_slips = frappe.get_list("Salary Slip", filters = { - "employee": employee, 'docstatus': 1 - }, - order_by = "start_date desc" + salary_slips = frappe.get_list( + "Salary Slip", filters={"employee": employee, "docstatus": 1}, order_by="start_date desc" ) if not salary_slips: return diff --git a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py index 771a6fea84a5..9396461f1d43 100644 --- a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py +++ b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py @@ -3,14 +3,9 @@ def get_data(): return { - 'fieldname': 'reference_name', - 'non_standard_fieldnames': { - 'Additional Salary': 'ref_docname', + "fieldname": "reference_name", + "non_standard_fieldnames": { + "Additional Salary": "ref_docname", }, - 'transactions': [ - { - 'label': _('Payment'), - 'items': ['Payment Entry', 'Additional Salary'] - } - ] + "transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Additional Salary"]}], } diff --git a/erpnext/payroll/doctype/gratuity/test_gratuity.py b/erpnext/payroll/doctype/gratuity/test_gratuity.py index 90e8061fed84..67bb447e9197 100644 --- a/erpnext/payroll/doctype/gratuity/test_gratuity.py +++ b/erpnext/payroll/doctype/gratuity/test_gratuity.py @@ -17,16 +17,18 @@ from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule test_dependencies = ["Salary Component", "Salary Slip", "Account"] + + class TestGratuity(unittest.TestCase): def setUp(self): frappe.db.delete("Gratuity") frappe.db.delete("Additional Salary", {"ref_doctype": "Gratuity"}) - make_earning_salary_component(setup=True, test_tax=True, company_list=['_Test Company']) - make_deduction_salary_component(setup=True, test_tax=True, company_list=['_Test Company']) + make_earning_salary_component(setup=True, test_tax=True, company_list=["_Test Company"]) + make_deduction_salary_component(setup=True, test_tax=True, company_list=["_Test Company"]) def test_get_last_salary_slip_should_return_none_for_new_employee(self): - new_employee = make_employee("new_employee@salary.com", company='_Test Company') + new_employee = make_employee("new_employee@salary.com", company="_Test Company") salary_slip = get_last_salary_slip(new_employee) assert salary_slip is None @@ -37,25 +39,32 @@ def test_check_gratuity_amount_based_on_current_slab_and_additional_salary_creat gratuity = create_gratuity(pay_via_salary_slip=1, employee=employee, rule=rule.name) # work experience calculation - date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date']) - employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days - - experience = employee_total_workings_days/rule.total_working_days_per_year + date_of_joining, relieving_date = frappe.db.get_value( + "Employee", employee, ["date_of_joining", "relieving_date"] + ) + employee_total_workings_days = ( + get_datetime(relieving_date) - get_datetime(date_of_joining) + ).days + + experience = employee_total_workings_days / rule.total_working_days_per_year gratuity.reload() from math import floor + self.assertEqual(floor(experience), gratuity.current_work_experience) - #amount Calculation - component_amount = frappe.get_all("Salary Detail", - filters={ - "docstatus": 1, - 'parent': sal_slip, - "parentfield": "earnings", - 'salary_component': "Basic Salary" - }, - fields=["amount"]) + # amount Calculation + component_amount = frappe.get_all( + "Salary Detail", + filters={ + "docstatus": 1, + "parent": sal_slip, + "parentfield": "earnings", + "salary_component": "Basic Salary", + }, + fields=["amount"], + ) - ''' 5 - 0 fraction is 1 ''' + """ 5 - 0 fraction is 1 """ gratuity_amount = component_amount[0].amount * experience gratuity.reload() @@ -70,13 +79,19 @@ def test_check_gratuity_amount_based_on_all_previous_slabs(self): rule = get_gratuity_rule("Rule Under Limited Contract (UAE)") set_mode_of_payment_account() - gratuity = create_gratuity(expense_account = 'Payment Account - _TC', mode_of_payment='Cash', employee=employee) + gratuity = create_gratuity( + expense_account="Payment Account - _TC", mode_of_payment="Cash", employee=employee + ) - #work experience calculation - date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date']) - employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days + # work experience calculation + date_of_joining, relieving_date = frappe.db.get_value( + "Employee", employee, ["date_of_joining", "relieving_date"] + ) + employee_total_workings_days = ( + get_datetime(relieving_date) - get_datetime(date_of_joining) + ).days - experience = employee_total_workings_days/rule.total_working_days_per_year + experience = employee_total_workings_days / rule.total_working_days_per_year gratuity.reload() @@ -84,29 +99,32 @@ def test_check_gratuity_amount_based_on_all_previous_slabs(self): self.assertEqual(floor(experience), gratuity.current_work_experience) - #amount Calculation - component_amount = frappe.get_all("Salary Detail", - filters={ - "docstatus": 1, - 'parent': sal_slip, - "parentfield": "earnings", - 'salary_component': "Basic Salary" - }, - fields=["amount"]) - - ''' range | Fraction + # amount Calculation + component_amount = frappe.get_all( + "Salary Detail", + filters={ + "docstatus": 1, + "parent": sal_slip, + "parentfield": "earnings", + "salary_component": "Basic Salary", + }, + fields=["amount"], + ) + + """ range | Fraction 0-1 | 0 1-5 | 0.7 5-0 | 1 - ''' + """ - gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) * component_amount[0].amount + gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) * component_amount[0].amount gratuity.reload() self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2)) self.assertEqual(gratuity.status, "Unpaid") from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry + pay_entry = get_payment_entry("Gratuity", gratuity.name) pay_entry.reference_no = "123467" pay_entry.reference_date = getdate() @@ -115,7 +133,7 @@ def test_check_gratuity_amount_based_on_all_previous_slabs(self): gratuity.reload() self.assertEqual(gratuity.status, "Paid") - self.assertEqual(flt(gratuity.paid_amount,2), flt(gratuity.amount, 2)) + self.assertEqual(flt(gratuity.paid_amount, 2), flt(gratuity.amount, 2)) def tearDown(self): frappe.db.rollback() @@ -127,14 +145,13 @@ def get_gratuity_rule(name): create_gratuity_rule() rule = frappe.get_doc("Gratuity Rule", name) rule.applicable_earnings_component = [] - rule.append("applicable_earnings_component", { - "salary_component": "Basic Salary" - }) + rule.append("applicable_earnings_component", {"salary_component": "Basic Salary"}) rule.save() rule.reload() return rule + def create_gratuity(**args): if args: args = frappe._dict(args) @@ -147,15 +164,16 @@ def create_gratuity(**args): gratuity.payroll_date = getdate() gratuity.salary_component = "Performance Bonus" else: - gratuity.expense_account = args.expense_account or 'Payment Account - _TC' + gratuity.expense_account = args.expense_account or "Payment Account - _TC" gratuity.payable_account = args.payable_account or get_payable_account("_Test Company") - gratuity.mode_of_payment = args.mode_of_payment or 'Cash' + gratuity.mode_of_payment = args.mode_of_payment or "Cash" gratuity.save() gratuity.submit() return gratuity + def set_mode_of_payment_account(): if not frappe.db.exists("Account", "Payment Account - _TC"): mode_of_payment = create_account() @@ -163,14 +181,15 @@ def set_mode_of_payment_account(): mode_of_payment = frappe.get_doc("Mode of Payment", "Cash") mode_of_payment.accounts = [] - mode_of_payment.append("accounts", { - "company": "_Test Company", - "default_account": "_Test Bank - _TC" - }) + mode_of_payment.append( + "accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"} + ) mode_of_payment.save() + def create_account(): - return frappe.get_doc({ + return frappe.get_doc( + { "doctype": "Account", "company": "_Test Company", "account_name": "Payment Account", @@ -179,13 +198,15 @@ def create_account(): "currency": "INR", "parent_account": "Bank Accounts - _TC", "account_type": "Bank", - }).insert(ignore_permissions=True) + } + ).insert(ignore_permissions=True) + def create_employee_and_get_last_salary_slip(): - employee = make_employee("test_employee@salary.com", company='_Test Company') + employee = make_employee("test_employee@salary.com", company="_Test Company") frappe.db.set_value("Employee", employee, "relieving_date", getdate()) - frappe.db.set_value("Employee", employee, "date_of_joining", add_days(getdate(), - (6*365))) - if not frappe.db.exists("Salary Slip", {"employee":employee}): + frappe.db.set_value("Employee", employee, "date_of_joining", add_days(getdate(), -(6 * 365))) + if not frappe.db.exists("Salary Slip", {"employee": employee}): salary_slip = make_employee_salary_slip("test_employee@salary.com", "Monthly") salary_slip.submit() salary_slip = salary_slip.name @@ -194,7 +215,10 @@ def create_employee_and_get_last_salary_slip(): if not frappe.db.get_value("Employee", "test_employee@salary.com", "holiday_list"): from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + make_holiday_list() - frappe.db.set_value("Company", '_Test Company', "default_holiday_list", "Salary Slip Test Holiday List") + frappe.db.set_value( + "Company", "_Test Company", "default_holiday_list", "Salary Slip Test Holiday List" + ) return employee, salary_slip diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py index d30cfc64848d..5cde79a16270 100644 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py +++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py @@ -8,25 +8,34 @@ class GratuityRule(Document): - def validate(self): for current_slab in self.gratuity_rule_slabs: if (current_slab.from_year > current_slab.to_year) and current_slab.to_year != 0: - frappe.throw(_("Row {0}: From (Year) can not be greater than To (Year)").format(current_slab.idx)) + frappe.throw( + _("Row {0}: From (Year) can not be greater than To (Year)").format(current_slab.idx) + ) + + if ( + current_slab.to_year == 0 and current_slab.from_year == 0 and len(self.gratuity_rule_slabs) > 1 + ): + frappe.throw( + _("You can not define multiple slabs if you have a slab with no lower and upper limits.") + ) - if current_slab.to_year == 0 and current_slab.from_year == 0 and len(self.gratuity_rule_slabs) > 1: - frappe.throw(_("You can not define multiple slabs if you have a slab with no lower and upper limits.")) def get_gratuity_rule(name, slabs, **args): args = frappe._dict(args) rule = frappe.new_doc("Gratuity Rule") rule.name = name - rule.calculate_gratuity_amount_based_on = args.calculate_gratuity_amount_based_on or "Current Slab" - rule.work_experience_calculation_method = args.work_experience_calculation_method or "Take Exact Completed Years" + rule.calculate_gratuity_amount_based_on = ( + args.calculate_gratuity_amount_based_on or "Current Slab" + ) + rule.work_experience_calculation_method = ( + args.work_experience_calculation_method or "Take Exact Completed Years" + ) rule.minimum_year_for_gratuity = 1 - for slab in slabs: slab = frappe._dict(slab) rule.append("gratuity_rule_slabs", slab) diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py index e7c67fbe11ba..fa5a9dedd35f 100644 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py +++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py @@ -3,11 +3,6 @@ def get_data(): return { - 'fieldname': 'gratuity_rule', - 'transactions': [ - { - 'label': _('Gratuity'), - 'items': ['Gratuity'] - } - ] + "fieldname": "gratuity_rule", + "transactions": [{"label": _("Gratuity"), "items": ["Gratuity"]}], } diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py index 040b2c893538..e62d61f4c2ff 100644 --- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py +++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py @@ -4,7 +4,7 @@ from frappe.model.document import Document -#import frappe +# import frappe import erpnext diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index a634dfe8c1a7..54d56f9612fb 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -29,11 +29,11 @@ class PayrollEntry(Document): def onload(self): - if not self.docstatus==1 or self.salary_slips_submitted: + if not self.docstatus == 1 or self.salary_slips_submitted: return # check if salary slips were manually submitted - entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name']) + entries = frappe.db.count("Salary Slip", {"payroll_entry": self.name, "docstatus": 1}, ["name"]) if cint(entries) == len(self.employees): self.set_onload("submitted_ss", True) @@ -52,33 +52,51 @@ def before_submit(self): def validate_employee_details(self): emp_with_sal_slip = [] for employee_details in self.employees: - if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": self.start_date, "end_date": self.end_date, "docstatus": 1}): + if frappe.db.exists( + "Salary Slip", + { + "employee": employee_details.employee, + "start_date": self.start_date, + "end_date": self.end_date, + "docstatus": 1, + }, + ): emp_with_sal_slip.append(employee_details.employee) if len(emp_with_sal_slip): frappe.throw(_("Salary Slip already exists for {0}").format(comma_and(emp_with_sal_slip))) def on_cancel(self): - frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip` - where payroll_entry=%s """, (self.name))) + frappe.delete_doc( + "Salary Slip", + frappe.db.sql_list( + """select name from `tabSalary Slip` + where payroll_entry=%s """, + (self.name), + ), + ) self.db_set("salary_slips_created", 0) self.db_set("salary_slips_submitted", 0) def get_emp_list(self): """ - Returns list of active employees based on selected criteria - and for which salary structure exists + Returns list of active employees based on selected criteria + and for which salary structure exists """ self.check_mandatory() filters = self.make_filters() cond = get_filter_condition(filters) cond += get_joining_relieving_condition(self.start_date, self.end_date) - condition = '' + condition = "" if self.payroll_frequency: - condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency} + condition = """and payroll_frequency = '%(payroll_frequency)s'""" % { + "payroll_frequency": self.payroll_frequency + } - sal_struct = get_sal_struct(self.company, self.currency, self.salary_slip_based_on_timesheet, condition) + sal_struct = get_sal_struct( + self.company, self.currency, self.salary_slip_based_on_timesheet, condition + ) if sal_struct: cond += "and t2.salary_structure IN %(sal_struct)s " cond += "and t2.payroll_payable_account = %(payroll_payable_account)s " @@ -89,20 +107,25 @@ def get_emp_list(self): def make_filters(self): filters = frappe._dict() - filters['company'] = self.company - filters['branch'] = self.branch - filters['department'] = self.department - filters['designation'] = self.designation + filters["company"] = self.company + filters["branch"] = self.branch + filters["department"] = self.department + filters["designation"] = self.designation return filters @frappe.whitelist() def fill_employee_details(self): - self.set('employees', []) + self.set("employees", []) employees = self.get_emp_list() if not employees: - error_msg = _("No employees found for the mentioned criteria:Project Name: " + project_name + "
Frequency: " + " " + frequency + "
Update Reminder:" + " " + str(date_start) + "
Expected Date End:" + " " + str(date_end) + "
Percent Progress:" + " " + str(progress) + "
Number of Updates:" + " " + str(len(update)) + "
" + "Number of drafts:" + " " + str(number_of_drafts) + "
" - msg += """Project ID | Date Updated | Time Updated | Project Status | Notes | """ - for updates in update: - msg += "
---|---|---|---|---|
" + str(updates[0]) + " | " + str(updates[1]) + " | " + str(updates[2]) + " | " + str(updates[3]) + " | " + "" + str(updates[4]) + " |
Hello,
Please help us send you GST Ready Invoices.
@@ -109,7 +123,9 @@ def _send_gstin_reminder(party_type, party, default_email_id=None, sent_to=None)
ERPNext is a free and open source ERP system.
Task due today:
\n\n\nThis is a notification for a task that is due today, and a sample Notification. In ERPNext you can setup notifications on anything, Invoices, Orders, Leads, Opportunities, so you never miss a thing.\n
To edit this, and setup other alerts, just type Notification in the search bar.