Merge branch 'version-14-hotfix' of https://github.com/frappe/erpnext into default_dates_in_reports
This commit is contained in:
@@ -9,6 +9,13 @@ trim_trailing_whitespace = true
|
|||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
# python, js indentation settings
|
# python, js indentation settings
|
||||||
[{*.py,*.js}]
|
[{*.py,*.js,*.vue,*.css,*.scss,*.html}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
max_line_length = 110
|
||||||
|
|
||||||
|
# JSON files - mostly doctype schema files
|
||||||
|
[{*.json}]
|
||||||
|
insert_final_newline = false
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|||||||
@@ -156,6 +156,7 @@
|
|||||||
"onScan": true,
|
"onScan": true,
|
||||||
"html2canvas": true,
|
"html2canvas": true,
|
||||||
"extend_cscript": true,
|
"extend_cscript": true,
|
||||||
"localforage": true
|
"localforage": true,
|
||||||
|
"Plaid": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,3 +29,6 @@ b147b85e6ac19a9220cd1e2958a6ebd99373283a
|
|||||||
|
|
||||||
# bulk format python code with black
|
# bulk format python code with black
|
||||||
baec607ff5905b1c67531096a9cf50ec7ff00a5d
|
baec607ff5905b1c67531096a9cf50ec7ff00a5d
|
||||||
|
|
||||||
|
# ruff
|
||||||
|
4d34b1ead73baf4c5430a2ecbe44b9e8468d7626
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Setup dependencies
|
- name: Setup dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/semantic-commits.yml
vendored
2
.github/workflows/semantic-commits.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 14
|
node-version: 20
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Check commit titles
|
- name: Check commit titles
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ fail_fast: false
|
|||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.0.1
|
rev: v4.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
files: "erpnext.*"
|
files: "erpnext.*"
|
||||||
@@ -15,28 +15,65 @@ repos:
|
|||||||
args: ['--branch', 'develop']
|
args: ['--branch', 'develop']
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: check-ast
|
- id: check-ast
|
||||||
|
- id: check-json
|
||||||
|
- id: check-toml
|
||||||
|
- id: check-yaml
|
||||||
|
- id: debug-statements
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
rev: 5.0.4
|
rev: v2.7.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: prettier
|
||||||
additional_dependencies: [
|
types_or: [javascript, vue, scss]
|
||||||
'flake8-bugbear',
|
# Ignore any files that might contain jinja / bundles
|
||||||
]
|
exclude: |
|
||||||
args: ['--config', '.github/helper/.flake8_strict']
|
(?x)^(
|
||||||
exclude: ".*setup.py$"
|
erpnext/public/dist/.*|
|
||||||
|
cypress/.*|
|
||||||
|
.*node_modules.*|
|
||||||
|
.*boilerplate.*|
|
||||||
|
erpnext/public/js/controllers/.*|
|
||||||
|
erpnext/templates/pages/order.js|
|
||||||
|
erpnext/templates/includes/.*|
|
||||||
|
.*/supplier_quotation.js|
|
||||||
|
.*/sales_taxes_and_charges_template.js|
|
||||||
|
.*/purchase_taxes_and_charges_template.js|
|
||||||
|
.*/subcontracting_order.js|
|
||||||
|
.*/landed_cost_voucher.js|
|
||||||
|
.*/payment_entry.js|
|
||||||
|
.*/loan_interest_accrual.js|
|
||||||
|
.*/loan_disbursement.js|
|
||||||
|
.*/loan_application.js|
|
||||||
|
.*/italy.js|
|
||||||
|
.*/sales_invoice.js|
|
||||||
|
.*/subcontracting_receipt.js|
|
||||||
|
.*/request_for_quotation.js|
|
||||||
|
.*/pos_profile.js|
|
||||||
|
.*/opportunity.js|
|
||||||
|
.*/quotation.js|
|
||||||
|
.*/sales_common.js|
|
||||||
|
.*/sales_order.js|
|
||||||
|
.*/pos_invoice.js|
|
||||||
|
.*/purchase_invoice.js|
|
||||||
|
.*/loan_repayment.js|
|
||||||
|
.*/material_request.js|
|
||||||
|
.*/purchase_receipt.js|
|
||||||
|
.*/delivery_note.js|
|
||||||
|
.*/loan.js|
|
||||||
|
.*/stock_entry.js|
|
||||||
|
.*/purchase_order.js|
|
||||||
|
.*/loan_write_off.js
|
||||||
|
)$
|
||||||
|
|
||||||
- repo: https://github.com/adityahase/black
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
|
rev: v0.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: ruff
|
||||||
additional_dependencies: ['click==8.0.4']
|
name: "Run ruff linter and apply fixes"
|
||||||
|
args: ["--fix"]
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/isort
|
- id: ruff-format
|
||||||
rev: 5.12.0
|
name: "Format Python code"
|
||||||
hooks:
|
|
||||||
- id: isort
|
|
||||||
exclude: ".*setup.py$"
|
|
||||||
|
|
||||||
|
|
||||||
ci:
|
ci:
|
||||||
|
|||||||
@@ -1,25 +1,13 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
parserPreset: 'conventional-changelog-conventionalcommits',
|
parserPreset: "conventional-changelog-conventionalcommits",
|
||||||
rules: {
|
rules: {
|
||||||
'subject-empty': [2, 'never'],
|
"subject-empty": [2, "never"],
|
||||||
'type-case': [2, 'always', 'lower-case'],
|
"type-case": [2, "always", "lower-case"],
|
||||||
'type-empty': [2, 'never'],
|
"type-empty": [2, "never"],
|
||||||
'type-enum': [
|
"type-enum": [
|
||||||
2,
|
2,
|
||||||
'always',
|
"always",
|
||||||
[
|
["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"],
|
||||||
'build',
|
|
||||||
'chore',
|
|
||||||
'ci',
|
|
||||||
'docs',
|
|
||||||
'feat',
|
|
||||||
'fix',
|
|
||||||
'perf',
|
|
||||||
'refactor',
|
|
||||||
'revert',
|
|
||||||
'style',
|
|
||||||
'test',
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import inspect
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
__version__ = "14.52.1"
|
__version__ = "14.67.1"
|
||||||
|
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
@@ -13,7 +13,7 @@ def get_default_company(user=None):
|
|||||||
if not user:
|
if not user:
|
||||||
user = frappe.session.user
|
user = frappe.session.user
|
||||||
|
|
||||||
companies = get_user_default_as_list(user, "company")
|
companies = get_user_default_as_list("company", user)
|
||||||
if companies:
|
if companies:
|
||||||
default_company = companies[0]
|
default_company = companies[0]
|
||||||
else:
|
else:
|
||||||
@@ -36,10 +36,8 @@ def get_default_cost_center(company):
|
|||||||
|
|
||||||
if not frappe.flags.company_cost_center:
|
if not frappe.flags.company_cost_center:
|
||||||
frappe.flags.company_cost_center = {}
|
frappe.flags.company_cost_center = {}
|
||||||
if not company in frappe.flags.company_cost_center:
|
if company not in frappe.flags.company_cost_center:
|
||||||
frappe.flags.company_cost_center[company] = frappe.get_cached_value(
|
frappe.flags.company_cost_center[company] = frappe.get_cached_value("Company", company, "cost_center")
|
||||||
"Company", company, "cost_center"
|
|
||||||
)
|
|
||||||
return frappe.flags.company_cost_center[company]
|
return frappe.flags.company_cost_center[company]
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +45,7 @@ def get_company_currency(company):
|
|||||||
"""Returns the default company currency"""
|
"""Returns the default company currency"""
|
||||||
if not frappe.flags.company_currency:
|
if not frappe.flags.company_currency:
|
||||||
frappe.flags.company_currency = {}
|
frappe.flags.company_currency = {}
|
||||||
if not company in frappe.flags.company_currency:
|
if company not in frappe.flags.company_currency:
|
||||||
frappe.flags.company_currency[company] = frappe.db.get_value(
|
frappe.flags.company_currency[company] = frappe.db.get_value(
|
||||||
"Company", company, "default_currency", cache=True
|
"Company", company, "default_currency", cache=True
|
||||||
)
|
)
|
||||||
@@ -81,7 +79,7 @@ def is_perpetual_inventory_enabled(company):
|
|||||||
if not hasattr(frappe.local, "enable_perpetual_inventory"):
|
if not hasattr(frappe.local, "enable_perpetual_inventory"):
|
||||||
frappe.local.enable_perpetual_inventory = {}
|
frappe.local.enable_perpetual_inventory = {}
|
||||||
|
|
||||||
if not company in frappe.local.enable_perpetual_inventory:
|
if company not in frappe.local.enable_perpetual_inventory:
|
||||||
frappe.local.enable_perpetual_inventory[company] = (
|
frappe.local.enable_perpetual_inventory[company] = (
|
||||||
frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
|
frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
|
||||||
)
|
)
|
||||||
@@ -96,7 +94,7 @@ def get_default_finance_book(company=None):
|
|||||||
if not hasattr(frappe.local, "default_finance_book"):
|
if not hasattr(frappe.local, "default_finance_book"):
|
||||||
frappe.local.default_finance_book = {}
|
frappe.local.default_finance_book = {}
|
||||||
|
|
||||||
if not company in frappe.local.default_finance_book:
|
if company not in frappe.local.default_finance_book:
|
||||||
frappe.local.default_finance_book[company] = frappe.get_cached_value(
|
frappe.local.default_finance_book[company] = frappe.get_cached_value(
|
||||||
"Company", company, "default_finance_book"
|
"Company", company, "default_finance_book"
|
||||||
)
|
)
|
||||||
@@ -108,7 +106,7 @@ def get_party_account_type(party_type):
|
|||||||
if not hasattr(frappe.local, "party_account_types"):
|
if not hasattr(frappe.local, "party_account_types"):
|
||||||
frappe.local.party_account_types = {}
|
frappe.local.party_account_types = {}
|
||||||
|
|
||||||
if not party_type in frappe.local.party_account_types:
|
if party_type not in frappe.local.party_account_types:
|
||||||
frappe.local.party_account_types[party_type] = (
|
frappe.local.party_account_types[party_type] = (
|
||||||
frappe.db.get_value("Party Type", party_type, "account_type") or ""
|
frappe.db.get_value("Party Type", party_type, "account_type") or ""
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ class ERPNextAddress(Address):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_reference()
|
self.validate_reference()
|
||||||
self.update_compnay_address()
|
self.update_compnay_address()
|
||||||
super(ERPNextAddress, self).validate()
|
super().validate()
|
||||||
|
|
||||||
def link_address(self):
|
def link_address(self):
|
||||||
"""Link address based on owner"""
|
"""Link address based on owner"""
|
||||||
if self.is_your_company_address:
|
if self.is_your_company_address:
|
||||||
return
|
return
|
||||||
|
|
||||||
return super(ERPNextAddress, self).link_address()
|
return super().link_address()
|
||||||
|
|
||||||
def update_compnay_address(self):
|
def update_compnay_address(self):
|
||||||
for link in self.get("links"):
|
for link in self.get("links"):
|
||||||
@@ -26,11 +26,11 @@ class ERPNextAddress(Address):
|
|||||||
self.is_your_company_address = 1
|
self.is_your_company_address = 1
|
||||||
|
|
||||||
def validate_reference(self):
|
def validate_reference(self):
|
||||||
if self.is_your_company_address and not [
|
if self.is_your_company_address and not [row for row in self.links if row.link_doctype == "Company"]:
|
||||||
row for row in self.links if row.link_doctype == "Company"
|
|
||||||
]:
|
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
|
_(
|
||||||
|
"Address needs to be linked to a Company. Please add a row for Company in the Links table."
|
||||||
|
),
|
||||||
title=_("Company Not Linked"),
|
title=_("Company Not Linked"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
frappe.provide('frappe.dashboards.chart_sources');
|
frappe.provide("frappe.dashboards.chart_sources");
|
||||||
|
|
||||||
frappe.dashboards.chart_sources["Account Balance Timeline"] = {
|
frappe.dashboards.chart_sources["Account Balance Timeline"] = {
|
||||||
method: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
|
method: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
|
||||||
@@ -9,14 +9,14 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = {
|
|||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Company",
|
options: "Company",
|
||||||
default: frappe.defaults.get_user_default("Company"),
|
default: frappe.defaults.get_user_default("Company"),
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: "account",
|
fieldname: "account",
|
||||||
label: __("Account"),
|
label: __("Account"),
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Account",
|
options: "Account",
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def get(
|
|||||||
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
|
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
|
||||||
|
|
||||||
account = filters.get("account")
|
account = filters.get("account")
|
||||||
company = filters.get("company")
|
filters.get("company")
|
||||||
|
|
||||||
if not account and chart_name:
|
if not account and chart_name:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
@@ -83,7 +83,6 @@ def build_result(account, dates, gl_entries):
|
|||||||
|
|
||||||
# get balances in debit
|
# get balances in debit
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
|
|
||||||
# entry date is after the current pointer, so move the pointer forward
|
# entry date is after the current pointer, so move the pointer forward
|
||||||
while getdate(entry.posting_date) > result[date_index][0]:
|
while getdate(entry.posting_date) > result[date_index][0]:
|
||||||
date_index += 1
|
date_index += 1
|
||||||
@@ -133,8 +132,6 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
|
|||||||
|
|
||||||
dates = [get_period_ending(from_date, timegrain)]
|
dates = [get_period_ending(from_date, timegrain)]
|
||||||
while getdate(dates[-1]) < getdate(to_date):
|
while getdate(dates[-1]) < getdate(to_date):
|
||||||
date = get_period_ending(
|
date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain)
|
||||||
add_to_date(dates[-1], years=years, months=months, days=days), timegrain
|
|
||||||
)
|
|
||||||
dates.append(date)
|
dates.append(date)
|
||||||
return dates
|
return dates
|
||||||
|
|||||||
@@ -24,14 +24,10 @@ from erpnext.accounts.utils import get_account_currency
|
|||||||
def validate_service_stop_date(doc):
|
def validate_service_stop_date(doc):
|
||||||
"""Validates service_stop_date for Purchase Invoice and Sales Invoice"""
|
"""Validates service_stop_date for Purchase Invoice and Sales Invoice"""
|
||||||
|
|
||||||
enable_check = (
|
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||||
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
|
||||||
)
|
|
||||||
|
|
||||||
old_stop_dates = {}
|
old_stop_dates = {}
|
||||||
old_doc = frappe.db.get_all(
|
old_doc = frappe.db.get_all(f"{doc.doctype} Item", {"parent": doc.name}, ["name", "service_stop_date"])
|
||||||
"{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
|
|
||||||
)
|
|
||||||
|
|
||||||
for d in old_doc:
|
for d in old_doc:
|
||||||
old_stop_dates[d.name] = d.service_stop_date or ""
|
old_stop_dates[d.name] = d.service_stop_date or ""
|
||||||
@@ -62,16 +58,14 @@ def build_conditions(process_type, account, company):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if account:
|
if account:
|
||||||
conditions += "AND %s='%s'" % (deferred_account, account)
|
conditions += f"AND {deferred_account}='{account}'"
|
||||||
elif company:
|
elif company:
|
||||||
conditions += f"AND p.company = {frappe.db.escape(company)}"
|
conditions += f"AND p.company = {frappe.db.escape(company)}"
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
|
|
||||||
def convert_deferred_expense_to_expense(
|
def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=""):
|
||||||
deferred_process, start_date=None, end_date=None, conditions=""
|
|
||||||
):
|
|
||||||
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||||
|
|
||||||
if not start_date:
|
if not start_date:
|
||||||
@@ -81,16 +75,14 @@ def convert_deferred_expense_to_expense(
|
|||||||
|
|
||||||
# check for the purchase invoice for which GL entries has to be done
|
# check for the purchase invoice for which GL entries has to be done
|
||||||
invoices = frappe.db.sql_list(
|
invoices = frappe.db.sql_list(
|
||||||
"""
|
f"""
|
||||||
select distinct item.parent
|
select distinct item.parent
|
||||||
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
|
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
|
||||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
where item.service_start_date<=%s and item.service_end_date>=%s
|
||||||
and item.enable_deferred_expense = 1 and item.parent=p.name
|
and item.enable_deferred_expense = 1 and item.parent=p.name
|
||||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||||
{0}
|
{conditions}
|
||||||
""".format(
|
""",
|
||||||
conditions
|
|
||||||
),
|
|
||||||
(end_date, start_date),
|
(end_date, start_date),
|
||||||
) # nosec
|
) # nosec
|
||||||
|
|
||||||
@@ -103,9 +95,7 @@ def convert_deferred_expense_to_expense(
|
|||||||
send_mail(deferred_process)
|
send_mail(deferred_process)
|
||||||
|
|
||||||
|
|
||||||
def convert_deferred_revenue_to_income(
|
def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=""):
|
||||||
deferred_process, start_date=None, end_date=None, conditions=""
|
|
||||||
):
|
|
||||||
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||||
|
|
||||||
if not start_date:
|
if not start_date:
|
||||||
@@ -115,16 +105,14 @@ def convert_deferred_revenue_to_income(
|
|||||||
|
|
||||||
# check for the sales invoice for which GL entries has to be done
|
# check for the sales invoice for which GL entries has to be done
|
||||||
invoices = frappe.db.sql_list(
|
invoices = frappe.db.sql_list(
|
||||||
"""
|
f"""
|
||||||
select distinct item.parent
|
select distinct item.parent
|
||||||
from `tabSales Invoice Item` item, `tabSales Invoice` p
|
from `tabSales Invoice Item` item, `tabSales Invoice` p
|
||||||
where item.service_start_date<=%s and item.service_end_date>=%s
|
where item.service_start_date<=%s and item.service_end_date>=%s
|
||||||
and item.enable_deferred_revenue = 1 and item.parent=p.name
|
and item.enable_deferred_revenue = 1 and item.parent=p.name
|
||||||
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
|
||||||
{0}
|
{conditions}
|
||||||
""".format(
|
""",
|
||||||
conditions
|
|
||||||
),
|
|
||||||
(end_date, start_date),
|
(end_date, start_date),
|
||||||
) # nosec
|
) # nosec
|
||||||
|
|
||||||
@@ -243,9 +231,7 @@ def calculate_monthly_amount(
|
|||||||
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||||
doc, item
|
doc, item
|
||||||
)
|
)
|
||||||
base_amount = flt(
|
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
|
||||||
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
|
|
||||||
)
|
|
||||||
if account_currency == doc.company_currency:
|
if account_currency == doc.company_currency:
|
||||||
amount = base_amount
|
amount = base_amount
|
||||||
else:
|
else:
|
||||||
@@ -265,17 +251,13 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
|
|||||||
if account_currency == doc.company_currency:
|
if account_currency == doc.company_currency:
|
||||||
amount = base_amount
|
amount = base_amount
|
||||||
else:
|
else:
|
||||||
amount = flt(
|
amount = flt(item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount"))
|
||||||
item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
|
||||||
doc, item
|
doc, item
|
||||||
)
|
)
|
||||||
|
|
||||||
base_amount = flt(
|
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
|
||||||
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
|
|
||||||
)
|
|
||||||
if account_currency == doc.company_currency:
|
if account_currency == doc.company_currency:
|
||||||
amount = base_amount
|
amount = base_amount
|
||||||
else:
|
else:
|
||||||
@@ -296,26 +278,22 @@ def get_already_booked_amount(doc, item):
|
|||||||
|
|
||||||
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
|
select sum({}) as total_credit, sum({}) as total_credit_in_account_currency, voucher_detail_no
|
||||||
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
|
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
|
||||||
and is_cancelled = 0
|
and is_cancelled = 0
|
||||||
group by voucher_detail_no
|
group by voucher_detail_no
|
||||||
""".format(
|
""".format(total_credit_debit, total_credit_debit_currency),
|
||||||
total_credit_debit, total_credit_debit_currency
|
|
||||||
),
|
|
||||||
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
journal_entry_details = frappe.db.sql(
|
journal_entry_details = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
|
SELECT sum(c.{}) as total_credit, sum(c.{}) as total_credit_in_account_currency, reference_detail_no
|
||||||
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
|
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
|
||||||
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
|
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
|
||||||
and p.docstatus < 2 group by reference_detail_no
|
and p.docstatus < 2 group by reference_detail_no
|
||||||
""".format(
|
""".format(total_credit_debit, total_credit_debit_currency),
|
||||||
total_credit_debit, total_credit_debit_currency
|
|
||||||
),
|
|
||||||
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
@@ -337,9 +315,7 @@ def get_already_booked_amount(doc, item):
|
|||||||
|
|
||||||
|
|
||||||
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||||
enable_check = (
|
enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
||||||
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
|
|
||||||
)
|
|
||||||
|
|
||||||
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
|
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
|
||||||
|
|
||||||
@@ -440,9 +416,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
via_journal_entry = cint(
|
via_journal_entry = cint(
|
||||||
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
|
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
|
||||||
)
|
)
|
||||||
submit_journal_entry = cint(
|
submit_journal_entry = cint(frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries"))
|
||||||
frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")
|
|
||||||
)
|
|
||||||
book_deferred_entries_based_on = frappe.db.get_singles_value(
|
book_deferred_entries_based_on = frappe.db.get_singles_value(
|
||||||
"Accounts Settings", "book_deferred_entries_based_on"
|
"Accounts Settings", "book_deferred_entries_based_on"
|
||||||
)
|
)
|
||||||
@@ -462,9 +436,7 @@ def process_deferred_accounting(posting_date=None):
|
|||||||
posting_date = today()
|
posting_date = today()
|
||||||
|
|
||||||
if not cint(
|
if not cint(
|
||||||
frappe.db.get_singles_value(
|
frappe.db.get_singles_value("Accounts Settings", "automatically_process_deferred_accounting_entry")
|
||||||
"Accounts Settings", "automatically_process_deferred_accounting_entry"
|
|
||||||
)
|
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -587,16 +559,13 @@ def book_revenue_via_journal_entry(
|
|||||||
deferred_process=None,
|
deferred_process=None,
|
||||||
submit="No",
|
submit="No",
|
||||||
):
|
):
|
||||||
|
|
||||||
if amount == 0:
|
if amount == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
journal_entry = frappe.new_doc("Journal Entry")
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
journal_entry.posting_date = posting_date
|
journal_entry.posting_date = posting_date
|
||||||
journal_entry.company = doc.company
|
journal_entry.company = doc.company
|
||||||
journal_entry.voucher_type = (
|
journal_entry.voucher_type = "Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
|
||||||
"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
|
|
||||||
)
|
|
||||||
journal_entry.process_deferred_accounting = deferred_process
|
journal_entry.process_deferred_accounting = deferred_process
|
||||||
|
|
||||||
debit_entry = {
|
debit_entry = {
|
||||||
@@ -645,7 +614,6 @@ def book_revenue_via_journal_entry(
|
|||||||
|
|
||||||
|
|
||||||
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
||||||
|
|
||||||
if doctype == "Sales Invoice":
|
if doctype == "Sales Invoice":
|
||||||
credit_account, debit_account = frappe.db.get_value(
|
credit_account, debit_account = frappe.db.get_value(
|
||||||
"Sales Invoice Item",
|
"Sales Invoice Item",
|
||||||
|
|||||||
@@ -1,33 +1,32 @@
|
|||||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Account', {
|
frappe.ui.form.on("Account", {
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
frm.add_fetch('parent_account', 'report_type', 'report_type');
|
frm.add_fetch("parent_account", "report_type", "report_type");
|
||||||
frm.add_fetch('parent_account', 'root_type', 'root_type');
|
frm.add_fetch("parent_account", "root_type", "root_type");
|
||||||
},
|
},
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
frm.set_query('parent_account', function(doc) {
|
frm.set_query("parent_account", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"is_group": 1,
|
is_group: 1,
|
||||||
"company": doc.company
|
company: doc.company,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frm.toggle_display('account_name', frm.is_new());
|
frm.toggle_display("account_name", frm.is_new());
|
||||||
|
|
||||||
// hide fields if group
|
// hide fields if group
|
||||||
frm.toggle_display(['account_type', 'tax_rate'], cint(frm.doc.is_group) == 0);
|
frm.toggle_display(["account_type", "tax_rate"], cint(frm.doc.is_group) == 0);
|
||||||
|
|
||||||
// disable fields
|
// disable fields
|
||||||
frm.toggle_enable(['is_group', 'company'], false);
|
frm.toggle_enable(["is_group", "company"], false);
|
||||||
|
|
||||||
if (cint(frm.doc.is_group) == 0) {
|
if (cint(frm.doc.is_group) == 0) {
|
||||||
frm.toggle_display('freeze_account', frm.doc.__onload
|
frm.toggle_display("freeze_account", frm.doc.__onload && frm.doc.__onload.can_freeze_account);
|
||||||
&& frm.doc.__onload.can_freeze_account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read-only for root accounts
|
// read-only for root accounts
|
||||||
@@ -38,79 +37,101 @@ frappe.ui.form.on('Account', {
|
|||||||
} else {
|
} else {
|
||||||
// credit days and type if customer or supplier
|
// credit days and type if customer or supplier
|
||||||
frm.set_intro(null);
|
frm.set_intro(null);
|
||||||
frm.trigger('account_type');
|
frm.trigger("account_type");
|
||||||
// show / hide convert buttons
|
// show / hide convert buttons
|
||||||
frm.trigger('add_toolbar_buttons');
|
frm.trigger("add_toolbar_buttons");
|
||||||
}
|
}
|
||||||
if (frm.has_perm('write')) {
|
if (frm.has_perm("write")) {
|
||||||
frm.add_custom_button(__('Merge Account'), function () {
|
frm.add_custom_button(
|
||||||
|
__("Merge Account"),
|
||||||
|
function () {
|
||||||
frm.trigger("merge_account");
|
frm.trigger("merge_account");
|
||||||
}, __('Actions'));
|
},
|
||||||
frm.add_custom_button(__('Update Account Name / Number'), function () {
|
__("Actions")
|
||||||
|
);
|
||||||
|
frm.add_custom_button(
|
||||||
|
__("Update Account Name / Number"),
|
||||||
|
function () {
|
||||||
frm.trigger("update_account_number");
|
frm.trigger("update_account_number");
|
||||||
}, __('Actions'));
|
},
|
||||||
|
__("Actions")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
account_type: function (frm) {
|
account_type: function (frm) {
|
||||||
if (frm.doc.is_group == 0) {
|
if (frm.doc.is_group == 0) {
|
||||||
frm.toggle_display(['tax_rate'], frm.doc.account_type == 'Tax');
|
frm.toggle_display(["tax_rate"], frm.doc.account_type == "Tax");
|
||||||
frm.toggle_display('warehouse', frm.doc.account_type == 'Stock');
|
frm.toggle_display("warehouse", frm.doc.account_type == "Stock");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
add_toolbar_buttons: function(frm) {
|
add_toolbar_buttons: function (frm) {
|
||||||
frm.add_custom_button(__('Chart of Accounts'), () => {
|
frm.add_custom_button(
|
||||||
|
__("Chart of Accounts"),
|
||||||
|
() => {
|
||||||
frappe.set_route("Tree", "Account");
|
frappe.set_route("Tree", "Account");
|
||||||
}, __('View'));
|
},
|
||||||
|
__("View")
|
||||||
|
);
|
||||||
|
|
||||||
if (frm.doc.is_group == 1) {
|
if (frm.doc.is_group == 1) {
|
||||||
frm.add_custom_button(__('Convert to Non-Group'), function () {
|
frm.add_custom_button(
|
||||||
|
__("Convert to Non-Group"),
|
||||||
|
function () {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
method: 'convert_group_to_ledger',
|
method: "convert_group_to_ledger",
|
||||||
callback: function() {
|
callback: function () {
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}, __('Actions'));
|
},
|
||||||
|
__("Actions")
|
||||||
} else if (cint(frm.doc.is_group) == 0
|
);
|
||||||
&& frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
|
} else if (cint(frm.doc.is_group) == 0 && frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
|
||||||
frm.add_custom_button(__('General Ledger'), function () {
|
frm.add_custom_button(
|
||||||
|
__("General Ledger"),
|
||||||
|
function () {
|
||||||
frappe.route_options = {
|
frappe.route_options = {
|
||||||
"account": frm.doc.name,
|
account: frm.doc.name,
|
||||||
"from_date": frappe.sys_defaults.year_start_date,
|
from_date: frappe.sys_defaults.year_start_date,
|
||||||
"to_date": frappe.sys_defaults.year_end_date,
|
to_date: frappe.sys_defaults.year_end_date,
|
||||||
"company": frm.doc.company
|
company: frm.doc.company,
|
||||||
};
|
};
|
||||||
frappe.set_route("query-report", "General Ledger");
|
frappe.set_route("query-report", "General Ledger");
|
||||||
}, __('View'));
|
},
|
||||||
|
__("View")
|
||||||
|
);
|
||||||
|
|
||||||
frm.add_custom_button(__('Convert to Group'), function () {
|
frm.add_custom_button(
|
||||||
|
__("Convert to Group"),
|
||||||
|
function () {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
method: 'convert_ledger_to_group',
|
method: "convert_ledger_to_group",
|
||||||
callback: function() {
|
callback: function () {
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}, __('Actions'));
|
},
|
||||||
|
__("Actions")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
merge_account: function(frm) {
|
merge_account: function (frm) {
|
||||||
var d = new frappe.ui.Dialog({
|
var d = new frappe.ui.Dialog({
|
||||||
title: __('Merge with Existing Account'),
|
title: __("Merge with Existing Account"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"label" : "Name",
|
label: "Name",
|
||||||
"fieldname": "name",
|
fieldname: "name",
|
||||||
"fieldtype": "Data",
|
fieldtype: "Data",
|
||||||
"reqd": 1,
|
reqd: 1,
|
||||||
"default": frm.doc.name
|
default: frm.doc.name,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function () {
|
||||||
var data = d.get_values();
|
var data = d.get_values();
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.account.account.merge_account",
|
method: "erpnext.accounts.doctype.account.account.merge_account",
|
||||||
@@ -118,42 +139,45 @@ frappe.ui.form.on('Account', {
|
|||||||
old: frm.doc.name,
|
old: frm.doc.name,
|
||||||
new: data.name,
|
new: data.name,
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if(!r.exc) {
|
if (!r.exc) {
|
||||||
if(r.message) {
|
if (r.message) {
|
||||||
frappe.set_route("Form", "Account", r.message);
|
frappe.set_route("Form", "Account", r.message);
|
||||||
}
|
}
|
||||||
d.hide();
|
d.hide();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
primary_action_label: __('Merge')
|
primary_action_label: __("Merge"),
|
||||||
});
|
});
|
||||||
d.show();
|
d.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
update_account_number: function(frm) {
|
update_account_number: function (frm) {
|
||||||
var d = new frappe.ui.Dialog({
|
var d = new frappe.ui.Dialog({
|
||||||
title: __('Update Account Number / Name'),
|
title: __("Update Account Number / Name"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"label": "Account Name",
|
label: "Account Name",
|
||||||
"fieldname": "account_name",
|
fieldname: "account_name",
|
||||||
"fieldtype": "Data",
|
fieldtype: "Data",
|
||||||
"reqd": 1,
|
reqd: 1,
|
||||||
"default": frm.doc.account_name
|
default: frm.doc.account_name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Account Number",
|
label: "Account Number",
|
||||||
"fieldname": "account_number",
|
fieldname: "account_number",
|
||||||
"fieldtype": "Data",
|
fieldtype: "Data",
|
||||||
"default": frm.doc.account_number
|
default: frm.doc.account_number,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function () {
|
||||||
var data = d.get_values();
|
var data = d.get_values();
|
||||||
if(data.account_number === frm.doc.account_number && data.account_name === frm.doc.account_name) {
|
if (
|
||||||
|
data.account_number === frm.doc.account_number &&
|
||||||
|
data.account_name === frm.doc.account_name
|
||||||
|
) {
|
||||||
d.hide();
|
d.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -163,11 +187,11 @@ frappe.ui.form.on('Account', {
|
|||||||
args: {
|
args: {
|
||||||
account_number: data.account_number,
|
account_number: data.account_number,
|
||||||
account_name: data.account_name,
|
account_name: data.account_name,
|
||||||
name: frm.doc.name
|
name: frm.doc.name,
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if(!r.exc) {
|
if (!r.exc) {
|
||||||
if(r.message) {
|
if (r.message) {
|
||||||
frappe.set_route("Form", "Account", r.message);
|
frappe.set_route("Form", "Account", r.message);
|
||||||
} else {
|
} else {
|
||||||
frm.set_value("account_number", data.account_number);
|
frm.set_value("account_number", data.account_number);
|
||||||
@@ -175,11 +199,11 @@ frappe.ui.form.on('Account', {
|
|||||||
}
|
}
|
||||||
d.hide();
|
d.hide();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
primary_action_label: __('Update')
|
primary_action_label: __("Update"),
|
||||||
});
|
});
|
||||||
d.show();
|
d.show();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class Account(NestedSet):
|
|||||||
if frappe.local.flags.ignore_update_nsm:
|
if frappe.local.flags.ignore_update_nsm:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
super(Account, self).on_update()
|
super().on_update()
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
frozen_accounts_modifier = frappe.db.get_value(
|
frozen_accounts_modifier = frappe.db.get_value(
|
||||||
@@ -58,6 +58,7 @@ class Account(NestedSet):
|
|||||||
self.validate_balance_must_be_debit_or_credit()
|
self.validate_balance_must_be_debit_or_credit()
|
||||||
self.validate_account_currency()
|
self.validate_account_currency()
|
||||||
self.validate_root_company_and_sync_account_to_children()
|
self.validate_root_company_and_sync_account_to_children()
|
||||||
|
self.validate_receivable_payable_account_type()
|
||||||
|
|
||||||
def validate_parent(self):
|
def validate_parent(self):
|
||||||
"""Fetch Parent Details and validate parent account"""
|
"""Fetch Parent Details and validate parent account"""
|
||||||
@@ -86,9 +87,7 @@ class Account(NestedSet):
|
|||||||
|
|
||||||
def set_root_and_report_type(self):
|
def set_root_and_report_type(self):
|
||||||
if self.parent_account:
|
if self.parent_account:
|
||||||
par = frappe.db.get_value(
|
par = frappe.db.get_value("Account", self.parent_account, ["report_type", "root_type"], as_dict=1)
|
||||||
"Account", self.parent_account, ["report_type", "root_type"], as_dict=1
|
|
||||||
)
|
|
||||||
|
|
||||||
if par.report_type:
|
if par.report_type:
|
||||||
self.report_type = par.report_type
|
self.report_type = par.report_type
|
||||||
@@ -114,6 +113,24 @@ class Account(NestedSet):
|
|||||||
"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
|
"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_receivable_payable_account_type(self):
|
||||||
|
doc_before_save = self.get_doc_before_save()
|
||||||
|
receivable_payable_types = ["Receivable", "Payable"]
|
||||||
|
if (
|
||||||
|
doc_before_save
|
||||||
|
and doc_before_save.account_type in receivable_payable_types
|
||||||
|
and doc_before_save.account_type != self.account_type
|
||||||
|
):
|
||||||
|
# check for ledger entries
|
||||||
|
if frappe.db.get_all("GL Entry", filters={"account": self.name, "is_cancelled": 0}, limit=1):
|
||||||
|
msg = _(
|
||||||
|
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
|
||||||
|
).format(
|
||||||
|
frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type
|
||||||
|
)
|
||||||
|
frappe.msgprint(msg)
|
||||||
|
self.add_comment("Comment", msg)
|
||||||
|
|
||||||
def validate_root_details(self):
|
def validate_root_details(self):
|
||||||
# does not exists parent
|
# does not exists parent
|
||||||
if frappe.db.exists("Account", self.name):
|
if frappe.db.exists("Account", self.name):
|
||||||
@@ -125,9 +142,7 @@ class Account(NestedSet):
|
|||||||
|
|
||||||
def validate_root_company_and_sync_account_to_children(self):
|
def validate_root_company_and_sync_account_to_children(self):
|
||||||
# ignore validation while creating new compnay or while syncing to child companies
|
# ignore validation while creating new compnay or while syncing to child companies
|
||||||
if (
|
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
|
||||||
frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
|
|
||||||
):
|
|
||||||
return
|
return
|
||||||
ancestors = get_root_company(self.company)
|
ancestors = get_root_company(self.company)
|
||||||
if ancestors:
|
if ancestors:
|
||||||
@@ -322,7 +337,7 @@ class Account(NestedSet):
|
|||||||
if self.check_gle_exists():
|
if self.check_gle_exists():
|
||||||
throw(_("Account with existing transaction can not be deleted"))
|
throw(_("Account with existing transaction can not be deleted"))
|
||||||
|
|
||||||
super(Account, self).on_trash(True)
|
super().on_trash(True)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -330,9 +345,8 @@ class Account(NestedSet):
|
|||||||
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
||||||
return frappe.db.sql(
|
return frappe.db.sql(
|
||||||
"""select name from tabAccount
|
"""select name from tabAccount
|
||||||
where is_group = 1 and docstatus != 2 and company = %s
|
where is_group = 1 and docstatus != 2 and company = {}
|
||||||
and %s like %s order by name limit %s offset %s"""
|
and {} like {} order by name limit {} offset {}""".format("%s", searchfield, "%s", "%s", "%s"),
|
||||||
% ("%s", searchfield, "%s", "%s", "%s"),
|
|
||||||
(filters["company"], "%%%s%%" % txt, page_len, start),
|
(filters["company"], "%%%s%%" % txt, page_len, start),
|
||||||
as_list=1,
|
as_list=1,
|
||||||
)
|
)
|
||||||
@@ -390,9 +404,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
|
|||||||
if not account:
|
if not account:
|
||||||
return
|
return
|
||||||
|
|
||||||
old_acc_name, old_acc_number = frappe.db.get_value(
|
old_acc_name, old_acc_number = frappe.db.get_value("Account", name, ["account_name", "account_number"])
|
||||||
"Account", name, ["account_name", "account_number"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# check if account exists in parent company
|
# check if account exists in parent company
|
||||||
ancestors = get_ancestors_of("Company", account.company)
|
ancestors = get_ancestors_of("Company", account.company)
|
||||||
@@ -500,7 +512,5 @@ def sync_update_account_number_in_child(
|
|||||||
if old_acc_number:
|
if old_acc_number:
|
||||||
filters["account_number"] = old_acc_number
|
filters["account_number"] = old_acc_number
|
||||||
|
|
||||||
for d in frappe.db.get_values(
|
for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True):
|
||||||
"Account", filters=filters, fieldname=["company", "name"], as_dict=True
|
|
||||||
):
|
|
||||||
update_account_number(d["name"], account_name, account_number, from_descendant=True)
|
update_account_number(d["name"], account_name, account_number, from_descendant=True)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
frappe.provide("frappe.treeview_settings")
|
frappe.provide("frappe.treeview_settings");
|
||||||
|
|
||||||
frappe.treeview_settings["Account"] = {
|
frappe.treeview_settings["Account"] = {
|
||||||
breadcrumb: "Accounts",
|
breadcrumb: "Accounts",
|
||||||
@@ -7,12 +7,12 @@ frappe.treeview_settings["Account"] = {
|
|||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
fieldname: "company",
|
fieldname: "company",
|
||||||
fieldtype:"Select",
|
fieldtype: "Select",
|
||||||
options: erpnext.utils.get_tree_options("company"),
|
options: erpnext.utils.get_tree_options("company"),
|
||||||
label: __("Company"),
|
label: __("Company"),
|
||||||
default: erpnext.utils.get_tree_default("company"),
|
default: erpnext.utils.get_tree_default("company"),
|
||||||
on_change: function() {
|
on_change: function () {
|
||||||
var me = frappe.treeview_settings['Account'].treeview;
|
var me = frappe.treeview_settings["Account"].treeview;
|
||||||
var company = me.page.fields_dict.company.get_value();
|
var company = me.page.fields_dict.company.get_value();
|
||||||
if (!company) {
|
if (!company) {
|
||||||
frappe.throw(__("Please set a Company"));
|
frappe.throw(__("Please set a Company"));
|
||||||
@@ -22,30 +22,36 @@ frappe.treeview_settings["Account"] = {
|
|||||||
args: {
|
args: {
|
||||||
company: company,
|
company: company,
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if(r.message) {
|
if (r.message) {
|
||||||
let root_company = r.message.length ? r.message[0] : "";
|
let root_company = r.message.length ? r.message[0] : "";
|
||||||
me.page.fields_dict.root_company.set_value(root_company);
|
me.page.fields_dict.root_company.set_value(root_company);
|
||||||
|
|
||||||
frappe.db.get_value("Company", {"name": company}, "allow_account_creation_against_child_company", (r) => {
|
frappe.db.get_value(
|
||||||
frappe.flags.ignore_root_company_validation = r.allow_account_creation_against_child_company;
|
"Company",
|
||||||
|
{ name: company },
|
||||||
|
"allow_account_creation_against_child_company",
|
||||||
|
(r) => {
|
||||||
|
frappe.flags.ignore_root_company_validation =
|
||||||
|
r.allow_account_creation_against_child_company;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: "root_company",
|
fieldname: "root_company",
|
||||||
fieldtype:"Data",
|
fieldtype: "Data",
|
||||||
label: __("Root Company"),
|
label: __("Root Company"),
|
||||||
hidden: true,
|
hidden: true,
|
||||||
disable_onchange: true
|
disable_onchange: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
root_label: "Accounts",
|
root_label: "Accounts",
|
||||||
get_tree_nodes: 'erpnext.accounts.utils.get_children',
|
get_tree_nodes: "erpnext.accounts.utils.get_children",
|
||||||
on_get_node: function(nodes, deep=false) {
|
on_get_node: function (nodes, deep = false) {
|
||||||
if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return;
|
if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return;
|
||||||
|
|
||||||
let accounts = [];
|
let accounts = [];
|
||||||
@@ -57,151 +63,231 @@ frappe.treeview_settings["Account"] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => {
|
frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => {
|
||||||
if(value) {
|
if (value) {
|
||||||
|
|
||||||
const get_balances = frappe.call({
|
const get_balances = frappe.call({
|
||||||
method: 'erpnext.accounts.utils.get_account_balances',
|
method: "erpnext.accounts.utils.get_account_balances",
|
||||||
args: {
|
args: {
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
company: cur_tree.args.company
|
company: cur_tree.args.company,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
get_balances.then(r => {
|
get_balances.then((r) => {
|
||||||
if (!r.message || r.message.length == 0) return;
|
if (!r.message || r.message.length == 0) return;
|
||||||
|
|
||||||
for (let account of r.message) {
|
for (let account of r.message) {
|
||||||
|
|
||||||
const node = cur_tree.nodes && cur_tree.nodes[account.value];
|
const node = cur_tree.nodes && cur_tree.nodes[account.value];
|
||||||
if (!node || node.is_root) continue;
|
if (!node || node.is_root) continue;
|
||||||
|
|
||||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||||
const balance = account.balance_in_account_currency || account.balance;
|
const balance = account.balance_in_account_currency || account.balance;
|
||||||
const dr_or_cr = balance > 0 ? "Dr": "Cr";
|
const dr_or_cr = balance > 0 ? "Dr" : "Cr";
|
||||||
const format = (value, currency) => format_currency(Math.abs(value), currency);
|
const format = (value, currency) => format_currency(Math.abs(value), currency);
|
||||||
|
|
||||||
if (account.balance!==undefined) {
|
if (account.balance !== undefined) {
|
||||||
node.parent && node.parent.find('.balance-area').remove();
|
node.parent && node.parent.find(".balance-area").remove();
|
||||||
$('<span class="balance-area pull-right">'
|
$(
|
||||||
+ (account.balance_in_account_currency ?
|
'<span class="balance-area pull-right">' +
|
||||||
(format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
|
(account.balance_in_account_currency
|
||||||
+ format(account.balance, account.company_currency)
|
? format(
|
||||||
+ " " + dr_or_cr
|
account.balance_in_account_currency,
|
||||||
+ '</span>').insertBefore(node.$ul);
|
account.account_currency
|
||||||
|
) + " / "
|
||||||
|
: "") +
|
||||||
|
format(account.balance, account.company_currency) +
|
||||||
|
" " +
|
||||||
|
dr_or_cr +
|
||||||
|
"</span>"
|
||||||
|
).insertBefore(node.$ul);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
add_tree_node: 'erpnext.accounts.utils.add_ac',
|
add_tree_node: "erpnext.accounts.utils.add_ac",
|
||||||
menu_items:[
|
menu_items: [
|
||||||
{
|
{
|
||||||
label: __('New Company'),
|
label: __("New Company"),
|
||||||
action: function() { frappe.new_doc("Company", true) },
|
action: function () {
|
||||||
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1'
|
frappe.new_doc("Company", true);
|
||||||
}
|
},
|
||||||
|
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
fields: [
|
fields: [
|
||||||
{fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true,
|
{
|
||||||
description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")},
|
fieldtype: "Data",
|
||||||
{fieldtype:'Data', fieldname:'account_number', label:__('Account Number'),
|
fieldname: "account_name",
|
||||||
description: __("Number of new Account, it will be included in the account name as a prefix")},
|
label: __("New Account Name"),
|
||||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
reqd: true,
|
||||||
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
description: __(
|
||||||
{fieldtype:'Select', fieldname:'root_type', label:__('Root Type'),
|
"Name of new Account. Note: Please don't create accounts for Customers and Suppliers"
|
||||||
options: ['Asset', 'Liability', 'Equity', 'Income', 'Expense'].join('\n'),
|
),
|
||||||
depends_on: 'eval:doc.is_group && !doc.parent_account'},
|
},
|
||||||
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
{
|
||||||
options: frappe.get_meta("Account").fields.filter(d => d.fieldname=='account_type')[0].options,
|
fieldtype: "Data",
|
||||||
description: __("Optional. This setting will be used to filter in various transactions.")
|
fieldname: "account_number",
|
||||||
|
label: __("Account Number"),
|
||||||
|
description: __("Number of new Account, it will be included in the account name as a prefix"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Check",
|
||||||
|
fieldname: "is_group",
|
||||||
|
label: __("Is Group"),
|
||||||
|
description: __(
|
||||||
|
"Further accounts can be made under Groups, but entries can be made against non-Groups"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Select",
|
||||||
|
fieldname: "root_type",
|
||||||
|
label: __("Root Type"),
|
||||||
|
options: ["Asset", "Liability", "Equity", "Income", "Expense"].join("\n"),
|
||||||
|
depends_on: "eval:doc.is_group && !doc.parent_account",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Select",
|
||||||
|
fieldname: "account_type",
|
||||||
|
label: __("Account Type"),
|
||||||
|
options: frappe.get_meta("Account").fields.filter((d) => d.fieldname == "account_type")[0]
|
||||||
|
.options,
|
||||||
|
description: __("Optional. This setting will be used to filter in various transactions."),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Float",
|
||||||
|
fieldname: "tax_rate",
|
||||||
|
label: __("Tax Rate"),
|
||||||
|
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Link",
|
||||||
|
fieldname: "account_currency",
|
||||||
|
label: __("Currency"),
|
||||||
|
options: "Currency",
|
||||||
|
description: __("Optional. Sets company's default currency, if not specified."),
|
||||||
},
|
},
|
||||||
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'),
|
|
||||||
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'},
|
|
||||||
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
|
|
||||||
description: __("Optional. Sets company's default currency, if not specified.")}
|
|
||||||
],
|
],
|
||||||
ignore_fields:["parent_account"],
|
ignore_fields: ["parent_account"],
|
||||||
onload: function(treeview) {
|
onload: function (treeview) {
|
||||||
frappe.treeview_settings['Account'].treeview = {};
|
frappe.treeview_settings["Account"].treeview = {};
|
||||||
$.extend(frappe.treeview_settings['Account'].treeview, treeview);
|
$.extend(frappe.treeview_settings["Account"].treeview, treeview);
|
||||||
function get_company() {
|
function get_company() {
|
||||||
return treeview.page.fields_dict.company.get_value();
|
return treeview.page.fields_dict.company.get_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
treeview.page.add_inner_button(__("Chart of Cost Centers"), function() {
|
treeview.page.add_inner_button(
|
||||||
frappe.set_route('Tree', 'Cost Center', {company: get_company()});
|
__("Chart of Cost Centers"),
|
||||||
}, __('View'));
|
function () {
|
||||||
|
frappe.set_route("Tree", "Cost Center", { company: get_company() });
|
||||||
|
},
|
||||||
|
__("View")
|
||||||
|
);
|
||||||
|
|
||||||
treeview.page.add_inner_button(__("Opening Invoice Creation Tool"), function() {
|
treeview.page.add_inner_button(
|
||||||
frappe.set_route('Form', 'Opening Invoice Creation Tool', {company: get_company()});
|
__("Opening Invoice Creation Tool"),
|
||||||
}, __('View'));
|
function () {
|
||||||
|
frappe.set_route("Form", "Opening Invoice Creation Tool", { company: get_company() });
|
||||||
|
},
|
||||||
|
__("View")
|
||||||
|
);
|
||||||
|
|
||||||
treeview.page.add_inner_button(__("Period Closing Voucher"), function() {
|
treeview.page.add_inner_button(
|
||||||
frappe.set_route('List', 'Period Closing Voucher', {company: get_company()});
|
__("Period Closing Voucher"),
|
||||||
}, __('View'));
|
function () {
|
||||||
|
frappe.set_route("List", "Period Closing Voucher", { company: get_company() });
|
||||||
|
},
|
||||||
|
__("View")
|
||||||
|
);
|
||||||
|
|
||||||
|
treeview.page.add_inner_button(
|
||||||
treeview.page.add_inner_button(__("Journal Entry"), function() {
|
__("Journal Entry"),
|
||||||
frappe.new_doc('Journal Entry', {company: get_company()});
|
function () {
|
||||||
}, __('Create'));
|
frappe.new_doc("Journal Entry", { company: get_company() });
|
||||||
treeview.page.add_inner_button(__("Company"), function() {
|
},
|
||||||
frappe.new_doc('Company');
|
__("Create")
|
||||||
}, __('Create'));
|
);
|
||||||
|
treeview.page.add_inner_button(
|
||||||
|
__("Company"),
|
||||||
|
function () {
|
||||||
|
frappe.new_doc("Company");
|
||||||
|
},
|
||||||
|
__("Create")
|
||||||
|
);
|
||||||
|
|
||||||
// financial statements
|
// financial statements
|
||||||
for (let report of ['Trial Balance', 'General Ledger', 'Balance Sheet',
|
for (let report of [
|
||||||
'Profit and Loss Statement', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
|
"Trial Balance",
|
||||||
treeview.page.add_inner_button(__(report), function() {
|
"General Ledger",
|
||||||
frappe.set_route('query-report', report, {company: get_company()});
|
"Balance Sheet",
|
||||||
}, __('Financial Statements'));
|
"Profit and Loss Statement",
|
||||||
}
|
"Cash Flow Statement",
|
||||||
|
"Accounts Payable",
|
||||||
|
"Accounts Receivable",
|
||||||
|
]) {
|
||||||
|
treeview.page.add_inner_button(
|
||||||
|
__(report),
|
||||||
|
function () {
|
||||||
|
frappe.set_route("query-report", report, { company: get_company() });
|
||||||
},
|
},
|
||||||
post_render: function(treeview) {
|
__("Financial Statements")
|
||||||
frappe.treeview_settings['Account'].treeview["tree"] = treeview.tree;
|
);
|
||||||
treeview.page.set_primary_action(__("New"), function() {
|
}
|
||||||
|
},
|
||||||
|
post_render: function (treeview) {
|
||||||
|
frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree;
|
||||||
|
treeview.page.set_primary_action(
|
||||||
|
__("New"),
|
||||||
|
function () {
|
||||||
let root_company = treeview.page.fields_dict.root_company.get_value();
|
let root_company = treeview.page.fields_dict.root_company.get_value();
|
||||||
|
|
||||||
if(root_company) {
|
if (root_company) {
|
||||||
frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
|
frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
|
||||||
} else {
|
} else {
|
||||||
treeview.new_node();
|
treeview.new_node();
|
||||||
}
|
}
|
||||||
}, "add");
|
},
|
||||||
|
"add"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
toolbar: [
|
toolbar: [
|
||||||
{
|
{
|
||||||
label:__("Add Child"),
|
label: __("Add Child"),
|
||||||
condition: function(node) {
|
condition: function (node) {
|
||||||
return frappe.boot.user.can_create.indexOf("Account") !== -1
|
return (
|
||||||
&& (!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value()
|
frappe.boot.user.can_create.indexOf("Account") !== -1 &&
|
||||||
|| frappe.flags.ignore_root_company_validation)
|
(!frappe.treeview_settings[
|
||||||
&& node.expandable && !node.hide_add;
|
"Account"
|
||||||
|
].treeview.page.fields_dict.root_company.get_value() ||
|
||||||
|
frappe.flags.ignore_root_company_validation) &&
|
||||||
|
node.expandable &&
|
||||||
|
!node.hide_add
|
||||||
|
);
|
||||||
},
|
},
|
||||||
click: function() {
|
click: function () {
|
||||||
var me = frappe.views.trees['Account'];
|
var me = frappe.views.trees["Account"];
|
||||||
me.new_node();
|
me.new_node();
|
||||||
},
|
},
|
||||||
btnClass: "hidden-xs"
|
btnClass: "hidden-xs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
condition: function(node) {
|
condition: function (node) {
|
||||||
return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1
|
return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1;
|
||||||
},
|
},
|
||||||
label: __("View Ledger"),
|
label: __("View Ledger"),
|
||||||
click: function(node, btn) {
|
click: function (node, btn) {
|
||||||
frappe.route_options = {
|
frappe.route_options = {
|
||||||
"account": node.label,
|
account: node.label,
|
||||||
"from_date": frappe.sys_defaults.year_start_date,
|
from_date: frappe.sys_defaults.year_start_date,
|
||||||
"to_date": frappe.sys_defaults.year_end_date,
|
to_date: frappe.sys_defaults.year_end_date,
|
||||||
"company": frappe.treeview_settings['Account'].treeview.page.fields_dict.company.get_value()
|
company:
|
||||||
|
frappe.treeview_settings["Account"].treeview.page.fields_dict.company.get_value(),
|
||||||
};
|
};
|
||||||
frappe.set_route("query-report", "General Ledger");
|
frappe.set_route("query-report", "General Ledger");
|
||||||
},
|
},
|
||||||
btnClass: "hidden-xs"
|
btnClass: "hidden-xs",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
extend_toolbar: true
|
extend_toolbar: true,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ def create_charts(
|
|||||||
"tax_rate",
|
"tax_rate",
|
||||||
"account_currency",
|
"account_currency",
|
||||||
]:
|
]:
|
||||||
|
|
||||||
account_number = cstr(child.get("account_number")).strip()
|
account_number = cstr(child.get("account_number")).strip()
|
||||||
account_name, account_name_in_db = add_suffix_if_duplicate(
|
account_name, account_name_in_db = add_suffix_if_duplicate(
|
||||||
account_name, account_number, accounts
|
account_name, account_number, accounts
|
||||||
@@ -39,7 +38,9 @@ def create_charts(
|
|||||||
|
|
||||||
is_group = identify_is_group(child)
|
is_group = identify_is_group(child)
|
||||||
report_type = (
|
report_type = (
|
||||||
"Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
|
"Balance Sheet"
|
||||||
|
if root_type in ["Asset", "Liability", "Equity"]
|
||||||
|
else "Profit and Loss"
|
||||||
)
|
)
|
||||||
|
|
||||||
account = frappe.get_doc(
|
account = frappe.get_doc(
|
||||||
@@ -141,7 +142,7 @@ def get_chart(chart_template, existing_company=None):
|
|||||||
for fname in os.listdir(path):
|
for fname in os.listdir(path):
|
||||||
fname = frappe.as_unicode(fname)
|
fname = frappe.as_unicode(fname)
|
||||||
if fname.endswith(".json"):
|
if fname.endswith(".json"):
|
||||||
with open(os.path.join(path, fname), "r") as f:
|
with open(os.path.join(path, fname)) as f:
|
||||||
chart = f.read()
|
chart = f.read()
|
||||||
if chart and json.loads(chart).get("name") == chart_template:
|
if chart and json.loads(chart).get("name") == chart_template:
|
||||||
return json.loads(chart).get("tree")
|
return json.loads(chart).get("tree")
|
||||||
@@ -173,7 +174,7 @@ def get_charts_for_country(country, with_standard=False):
|
|||||||
for fname in os.listdir(path):
|
for fname in os.listdir(path):
|
||||||
fname = frappe.as_unicode(fname)
|
fname = frappe.as_unicode(fname)
|
||||||
if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"):
|
if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"):
|
||||||
with open(os.path.join(path, fname), "r") as f:
|
with open(os.path.join(path, fname)) as f:
|
||||||
_get_chart_name(f.read())
|
_get_chart_name(f.read())
|
||||||
|
|
||||||
# if more than one charts, returned then add the standard
|
# if more than one charts, returned then add the standard
|
||||||
@@ -247,7 +248,13 @@ def validate_bank_account(coa, bank_account):
|
|||||||
|
|
||||||
def _get_account_names(account_master):
|
def _get_account_names(account_master):
|
||||||
for account_name, child in account_master.items():
|
for account_name, child in account_master.items():
|
||||||
if account_name not in ["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)
|
accounts.append(account_name)
|
||||||
|
|
||||||
_get_account_names(child)
|
_get_account_names(child)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ def go():
|
|||||||
default_account_types = get_default_account_types()
|
default_account_types = get_default_account_types()
|
||||||
|
|
||||||
country_dirs = []
|
country_dirs = []
|
||||||
for basepath, folders, files in os.walk(path):
|
for basepath, _folders, _files in os.walk(path):
|
||||||
basename = os.path.basename(basepath)
|
basename = os.path.basename(basepath)
|
||||||
if basename.startswith("l10n_"):
|
if basename.startswith("l10n_"):
|
||||||
country_dirs.append(basename)
|
country_dirs.append(basename)
|
||||||
@@ -35,9 +35,7 @@ def go():
|
|||||||
accounts, charts = {}, {}
|
accounts, charts = {}, {}
|
||||||
country_path = os.path.join(path, country_dir)
|
country_path = os.path.join(path, country_dir)
|
||||||
manifest = ast.literal_eval(open(os.path.join(country_path, "__openerp__.py")).read())
|
manifest = ast.literal_eval(open(os.path.join(country_path, "__openerp__.py")).read())
|
||||||
data_files = (
|
data_files = manifest.get("data", []) + manifest.get("init_xml", []) + manifest.get("update_xml", [])
|
||||||
manifest.get("data", []) + manifest.get("init_xml", []) + manifest.get("update_xml", [])
|
|
||||||
)
|
|
||||||
files_path = [os.path.join(country_path, d) for d in data_files]
|
files_path = [os.path.join(country_path, d) for d in data_files]
|
||||||
xml_roots = get_xml_roots(files_path)
|
xml_roots = get_xml_roots(files_path)
|
||||||
csv_content = get_csv_contents(files_path)
|
csv_content = get_csv_contents(files_path)
|
||||||
@@ -90,10 +88,10 @@ def get_csv_contents(files_path):
|
|||||||
fname = os.path.basename(filepath)
|
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"):
|
if fname.startswith(file_type) and fname.endswith(".csv"):
|
||||||
with open(filepath, "r") as csvfile:
|
with open(filepath) as csvfile:
|
||||||
try:
|
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:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
return csv_content
|
return csv_content
|
||||||
|
|
||||||
@@ -138,7 +136,7 @@ def get_account_types(root_list, csv_content, prefix=None):
|
|||||||
|
|
||||||
if csv_content and csv_content[0][0] == "id":
|
if csv_content and csv_content[0][0] == "id":
|
||||||
for row in csv_content[1:]:
|
for row in csv_content[1:]:
|
||||||
row_dict = dict(zip(csv_content[0], row))
|
row_dict = dict(zip(csv_content[0], row, strict=False))
|
||||||
data = {}
|
data = {}
|
||||||
if row_dict.get("code") and account_type_map.get(row_dict["code"]):
|
if row_dict.get("code") and account_type_map.get(row_dict["code"]):
|
||||||
data["account_type"] = account_type_map[row_dict["code"]]
|
data["account_type"] = account_type_map[row_dict["code"]]
|
||||||
@@ -150,7 +148,7 @@ def get_account_types(root_list, csv_content, prefix=None):
|
|||||||
|
|
||||||
def make_maps_for_xml(xml_roots, account_types, country_dir):
|
def make_maps_for_xml(xml_roots, account_types, country_dir):
|
||||||
"""make maps for `charts` and `accounts`"""
|
"""make maps for `charts` and `accounts`"""
|
||||||
for model, root_list in xml_roots.items():
|
for _model, root_list in xml_roots.items():
|
||||||
for root in root_list:
|
for root in root_list:
|
||||||
for node in root[0].findall("record"):
|
for node in root[0].findall("record"):
|
||||||
if node.get("model") == "account.account.template":
|
if node.get("model") == "account.account.template":
|
||||||
@@ -186,7 +184,7 @@ def make_maps_for_xml(xml_roots, account_types, country_dir):
|
|||||||
def make_maps_for_csv(csv_content, account_types, country_dir):
|
def make_maps_for_csv(csv_content, account_types, country_dir):
|
||||||
for content in csv_content.get("account.account.template", []):
|
for content in csv_content.get("account.account.template", []):
|
||||||
for row in content[1:]:
|
for row in content[1:]:
|
||||||
data = dict(zip(content[0], row))
|
data = dict(zip(content[0], row, strict=False))
|
||||||
account = {
|
account = {
|
||||||
"name": data.get("name"),
|
"name": data.get("name"),
|
||||||
"parent_id": data.get("parent_id:id") or data.get("parent_id/id"),
|
"parent_id": data.get("parent_id:id") or data.get("parent_id/id"),
|
||||||
@@ -206,7 +204,7 @@ def make_maps_for_csv(csv_content, account_types, country_dir):
|
|||||||
for content in csv_content.get("account.chart.template", []):
|
for content in csv_content.get("account.chart.template", []):
|
||||||
for row in content[1:]:
|
for row in content[1:]:
|
||||||
if row:
|
if row:
|
||||||
data = dict(zip(content[0], row))
|
data = dict(zip(content[0], row, strict=False))
|
||||||
charts.setdefault(data.get("id"), {}).update(
|
charts.setdefault(data.get("id"), {}).update(
|
||||||
{
|
{
|
||||||
"account_root_id": data.get("account_root_id:id") or data.get("account_root_id/id"),
|
"account_root_id": data.get("account_root_id:id") or data.get("account_root_id/id"),
|
||||||
@@ -241,7 +239,7 @@ def make_charts():
|
|||||||
if not src.get("name") or not src.get("account_root_id"):
|
if not src.get("name") or not src.get("account_root_id"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not src["account_root_id"] in accounts:
|
if src["account_root_id"] not in accounts:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
filename = src["id"][5:] + "_" + chart_id
|
filename = src["id"][5:] + "_" + chart_id
|
||||||
@@ -255,14 +253,20 @@ def make_charts():
|
|||||||
for key, val in chart["tree"].items():
|
for key, val in chart["tree"].items():
|
||||||
if key in ["name", "parent_id"]:
|
if key in ["name", "parent_id"]:
|
||||||
chart["tree"].pop(key)
|
chart["tree"].pop(key)
|
||||||
if type(val) == dict:
|
if isinstance(val, dict):
|
||||||
val["root_type"] = ""
|
val["root_type"] = ""
|
||||||
if chart:
|
if chart:
|
||||||
fpath = os.path.join(
|
fpath = os.path.join(
|
||||||
"erpnext", "erpnext", "accounts", "doctype", "account", "chart_of_accounts", filename + ".json"
|
"erpnext",
|
||||||
|
"erpnext",
|
||||||
|
"accounts",
|
||||||
|
"doctype",
|
||||||
|
"account",
|
||||||
|
"chart_of_accounts",
|
||||||
|
filename + ".json",
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(fpath, "r") as chartfile:
|
with open(fpath) as chartfile:
|
||||||
old_content = chartfile.read()
|
old_content = chartfile.read()
|
||||||
if not old_content or (
|
if not old_content or (
|
||||||
json.loads(old_content).get("is_active", "No") == "No"
|
json.loads(old_content).get("is_active", "No") == "No"
|
||||||
|
|||||||
@@ -53,8 +53,13 @@
|
|||||||
},
|
},
|
||||||
"II. Forderungen und sonstige Vermögensgegenstände": {
|
"II. Forderungen und sonstige Vermögensgegenstände": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Ford. a. Lieferungen und Leistungen": {
|
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
|
||||||
"account_number": "1400",
|
"account_number": "1400",
|
||||||
|
"account_type": "Receivable",
|
||||||
|
"is_group": 1
|
||||||
|
},
|
||||||
|
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
|
||||||
|
"account_number": "1410",
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Durchlaufende Posten": {
|
"Durchlaufende Posten": {
|
||||||
@@ -180,8 +185,13 @@
|
|||||||
},
|
},
|
||||||
"IV. Verbindlichkeiten aus Lieferungen und Leistungen": {
|
"IV. Verbindlichkeiten aus Lieferungen und Leistungen": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Verbindlichkeiten aus Lieferungen u. Leistungen": {
|
"Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": {
|
||||||
"account_number": "1600",
|
"account_number": "1600",
|
||||||
|
"account_type": "Payable",
|
||||||
|
"is_group": 1
|
||||||
|
},
|
||||||
|
"Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": {
|
||||||
|
"account_number": "1610",
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -407,13 +407,10 @@
|
|||||||
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
|
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
|
||||||
"account_number": "9960"
|
"account_number": "9960"
|
||||||
},
|
},
|
||||||
"Debitoren": {
|
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
|
||||||
"is_group": 1,
|
|
||||||
"account_number": "10000"
|
|
||||||
},
|
|
||||||
"Forderungen aus Lieferungen und Leistungen": {
|
|
||||||
"account_number": "1200",
|
"account_number": "1200",
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable",
|
||||||
|
"is_group": 1
|
||||||
},
|
},
|
||||||
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
|
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
|
||||||
"account_number": "1210"
|
"account_number": "1210"
|
||||||
@@ -1138,18 +1135,15 @@
|
|||||||
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
|
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
|
||||||
"account_number": "9964"
|
"account_number": "9964"
|
||||||
},
|
},
|
||||||
"Kreditoren": {
|
"Verb. aus Lieferungen und Leistungen mit Kontokorrent": {
|
||||||
"account_number": "70000",
|
"account_number": "3300",
|
||||||
|
"account_type": "Payable",
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"Wareneingangs-Verrechnungskonto" : {
|
"Wareneingangs-Verrechnungskonto" : {
|
||||||
"account_number": "70001",
|
"account_number": "70001",
|
||||||
"account_type": "Stock Received But Not Billed"
|
"account_type": "Stock Received But Not Billed"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Verb. aus Lieferungen und Leistungen": {
|
|
||||||
"account_number": "3300",
|
|
||||||
"account_type": "Payable"
|
|
||||||
},
|
|
||||||
"Verb. aus Lieferungen und Leistungen ohne Kontokorrent": {
|
"Verb. aus Lieferungen und Leistungen ohne Kontokorrent": {
|
||||||
"account_number": "3310"
|
"account_number": "3310"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -260,28 +260,20 @@ class TestAccount(unittest.TestCase):
|
|||||||
acc.insert()
|
acc.insert()
|
||||||
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
frappe.db.exists(
|
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 4"})
|
||||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
frappe.db.exists(
|
frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 5"})
|
||||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Try renaming child company account
|
# Try renaming child company account
|
||||||
acc_tc_5 = frappe.db.get_value(
|
acc_tc_5 = frappe.db.get_value(
|
||||||
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
|
||||||
)
|
)
|
||||||
self.assertRaises(
|
self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account")
|
||||||
frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Rename child company account with allow_account_creation_against_child_company enabled
|
# Rename child company account with allow_account_creation_against_child_company enabled
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1)
|
||||||
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 1
|
|
||||||
)
|
|
||||||
|
|
||||||
update_account_number(acc_tc_5, "Test Modified Account")
|
update_account_number(acc_tc_5, "Test Modified Account")
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
@@ -290,9 +282,7 @@ class TestAccount(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
|
||||||
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
|
|
||||||
)
|
|
||||||
|
|
||||||
to_delete = [
|
to_delete = [
|
||||||
"Test Group Account - _TC3",
|
"Test Group Account - _TC3",
|
||||||
@@ -317,9 +307,7 @@ class TestAccount(unittest.TestCase):
|
|||||||
self.assertEqual(acc.account_currency, "INR")
|
self.assertEqual(acc.account_currency, "INR")
|
||||||
|
|
||||||
# Make a JV against this account
|
# Make a JV against this account
|
||||||
make_journal_entry(
|
make_journal_entry("Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True)
|
||||||
"Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True
|
|
||||||
)
|
|
||||||
|
|
||||||
acc.account_currency = "USD"
|
acc.account_currency = "USD"
|
||||||
self.assertRaises(frappe.ValidationError, acc.save)
|
self.assertRaises(frappe.ValidationError, acc.save)
|
||||||
|
|||||||
@@ -17,16 +17,12 @@ class AccountClosingBalance(Document):
|
|||||||
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
|
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
|
||||||
accounting_dimensions = get_accounting_dimensions()
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
previous_closing_entries = get_previous_closing_entries(
|
previous_closing_entries = get_previous_closing_entries(company, closing_date, accounting_dimensions)
|
||||||
company, closing_date, accounting_dimensions
|
|
||||||
)
|
|
||||||
combined_entries = closing_entries + previous_closing_entries
|
combined_entries = closing_entries + previous_closing_entries
|
||||||
|
|
||||||
merged_entries = aggregate_with_last_account_closing_balance(
|
merged_entries = aggregate_with_last_account_closing_balance(combined_entries, accounting_dimensions)
|
||||||
combined_entries, accounting_dimensions
|
|
||||||
)
|
|
||||||
|
|
||||||
for key, value in merged_entries.items():
|
for _key, value in merged_entries.items():
|
||||||
cle = frappe.new_doc("Account Closing Balance")
|
cle = frappe.new_doc("Account Closing Balance")
|
||||||
cle.update(value)
|
cle.update(value)
|
||||||
cle.update(value["dimensions"])
|
cle.update(value["dimensions"])
|
||||||
|
|||||||
@@ -1,74 +1,86 @@
|
|||||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Accounting Dimension', {
|
frappe.ui.form.on("Accounting Dimension", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frm.set_query('document_type', () => {
|
frm.set_query("document_type", () => {
|
||||||
let invalid_doctypes = frappe.model.core_doctypes_list;
|
let invalid_doctypes = frappe.model.core_doctypes_list;
|
||||||
invalid_doctypes.push('Accounting Dimension', 'Project',
|
invalid_doctypes.push(
|
||||||
'Cost Center', 'Accounting Dimension Detail', 'Company');
|
"Accounting Dimension",
|
||||||
|
"Project",
|
||||||
|
"Cost Center",
|
||||||
|
"Accounting Dimension Detail",
|
||||||
|
"Company"
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
name: ['not in', invalid_doctypes]
|
name: ["not in", invalid_doctypes],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) {
|
frm.set_query("offsetting_account", "dimension_defaults", function (doc, cdt, cdn) {
|
||||||
let d = locals[cdt][cdn];
|
let d = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: d.company,
|
company: d.company,
|
||||||
root_type: ["in", ["Asset", "Liability"]],
|
root_type: ["in", ["Asset", "Liability"]],
|
||||||
is_group: 0
|
is_group: 0,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!frm.is_new()) {
|
if (!frm.is_new()) {
|
||||||
frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () {
|
frm.add_custom_button(__("Show {0}", [frm.doc.document_type]), function () {
|
||||||
frappe.set_route("List", frm.doc.document_type);
|
frappe.set_route("List", frm.doc.document_type);
|
||||||
});
|
});
|
||||||
|
|
||||||
let button = frm.doc.disabled ? "Enable" : "Disable";
|
let button = frm.doc.disabled ? "Enable" : "Disable";
|
||||||
|
|
||||||
frm.add_custom_button(__(button), function() {
|
frm.add_custom_button(__(button), function () {
|
||||||
|
frm.set_value("disabled", 1 - frm.doc.disabled);
|
||||||
frm.set_value('disabled', 1 - frm.doc.disabled);
|
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
|
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
|
||||||
args: {
|
args: {
|
||||||
doc: frm.doc
|
doc: frm.doc,
|
||||||
},
|
},
|
||||||
freeze: true,
|
freeze: true,
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled";
|
let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled";
|
||||||
frm.save();
|
frm.save();
|
||||||
frappe.show_alert({message:__(message), indicator:'green'});
|
frappe.show_alert({ message: __(message), indicator: "green" });
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
document_type: function(frm) {
|
document_type: function (frm) {
|
||||||
|
frm.set_value("label", frm.doc.document_type);
|
||||||
|
frm.set_value("fieldname", frappe.model.scrub(frm.doc.document_type));
|
||||||
|
|
||||||
frm.set_value('label', frm.doc.document_type);
|
frappe.db.get_value(
|
||||||
frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type));
|
"Accounting Dimension",
|
||||||
|
{ document_type: frm.doc.document_type },
|
||||||
frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => {
|
"document_type",
|
||||||
|
(r) => {
|
||||||
if (r && r.document_type) {
|
if (r && r.document_type) {
|
||||||
frm.set_df_property('document_type', 'description', "Document type is already set as dimension");
|
frm.set_df_property(
|
||||||
|
"document_type",
|
||||||
|
"description",
|
||||||
|
"Document type is already set as dimension"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on('Accounting Dimension Detail', {
|
frappe.ui.form.on("Accounting Dimension Detail", {
|
||||||
dimension_defaults_add: function(frm, cdt, cdn) {
|
dimension_defaults_add: function (frm, cdt, cdn) {
|
||||||
let row = locals[cdt][cdn];
|
let row = locals[cdt][cdn];
|
||||||
row.reference_document = frm.doc.document_type;
|
row.reference_document = frm.doc.document_type;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ class AccountingDimension(Document):
|
|||||||
self.set_fieldname_and_label()
|
self.set_fieldname_and_label()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.document_type in core_doctypes_list + (
|
if self.document_type in (
|
||||||
|
*core_doctypes_list,
|
||||||
"Accounting Dimension",
|
"Accounting Dimension",
|
||||||
"Project",
|
"Project",
|
||||||
"Cost Center",
|
"Cost Center",
|
||||||
@@ -25,13 +26,10 @@ class AccountingDimension(Document):
|
|||||||
"Company",
|
"Company",
|
||||||
"Account",
|
"Account",
|
||||||
):
|
):
|
||||||
|
|
||||||
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
|
||||||
exists = frappe.db.get_value(
|
exists = frappe.db.get_value("Accounting Dimension", {"document_type": self.document_type}, ["name"])
|
||||||
"Accounting Dimension", {"document_type": self.document_type}, ["name"]
|
|
||||||
)
|
|
||||||
|
|
||||||
if exists and self.is_new():
|
if exists and self.is_new():
|
||||||
frappe.throw(_("Document Type already used as a dimension"))
|
frappe.throw(_("Document Type already used as a dimension"))
|
||||||
@@ -89,7 +87,6 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
|||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
for doctype in doclist:
|
for doctype in doclist:
|
||||||
|
|
||||||
if (doc_count + 1) % 2 == 0:
|
if (doc_count + 1) % 2 == 0:
|
||||||
insert_after_field = "dimension_col_break"
|
insert_after_field = "dimension_col_break"
|
||||||
else:
|
else:
|
||||||
@@ -123,7 +120,7 @@ def add_dimension_to_budget_doctype(df, doc):
|
|||||||
df.update(
|
df.update(
|
||||||
{
|
{
|
||||||
"insert_after": "cost_center",
|
"insert_after": "cost_center",
|
||||||
"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type),
|
"depends_on": f"eval:doc.budget_against == '{doc.document_type}'",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -157,19 +154,17 @@ def delete_accounting_dimension(doc):
|
|||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
DELETE FROM `tabCustom Field`
|
DELETE FROM `tabCustom Field`
|
||||||
WHERE fieldname = %s
|
WHERE fieldname = {}
|
||||||
AND dt IN (%s)"""
|
AND dt IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||||
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
tuple([doc.fieldname, *doclist]),
|
||||||
tuple([doc.fieldname] + doclist),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
DELETE FROM `tabProperty Setter`
|
DELETE FROM `tabProperty Setter`
|
||||||
WHERE field_name = %s
|
WHERE field_name = {}
|
||||||
AND doc_type IN (%s)"""
|
AND doc_type IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
||||||
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
|
tuple([doc.fieldname, *doclist]),
|
||||||
tuple([doc.fieldname] + doclist),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
|
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
|
||||||
@@ -218,7 +213,6 @@ def get_doctypes_with_dimensions():
|
|||||||
|
|
||||||
|
|
||||||
def get_accounting_dimensions(as_list=True, filters=None):
|
def get_accounting_dimensions(as_list=True, filters=None):
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {"disabled": 0}
|
filters = {"disabled": 0}
|
||||||
|
|
||||||
@@ -236,18 +230,19 @@ def get_accounting_dimensions(as_list=True, filters=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_checks_for_pl_and_bs_accounts():
|
def get_checks_for_pl_and_bs_accounts():
|
||||||
dimensions = frappe.db.sql(
|
if frappe.flags.accounting_dimensions_details is None:
|
||||||
|
# nosemgrep
|
||||||
|
frappe.flags.accounting_dimensions_details = frappe.db.sql(
|
||||||
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
|
"""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
|
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
|
||||||
WHERE p.name = c.parent""",
|
WHERE p.name = c.parent""",
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
return dimensions
|
return frappe.flags.accounting_dimensions_details
|
||||||
|
|
||||||
|
|
||||||
def get_dimension_with_children(doctype, dimensions):
|
def get_dimension_with_children(doctype, dimensions):
|
||||||
|
|
||||||
if isinstance(dimensions, str):
|
if isinstance(dimensions, str):
|
||||||
dimensions = [dimensions]
|
dimensions = [dimensions]
|
||||||
|
|
||||||
@@ -255,9 +250,7 @@ def get_dimension_with_children(doctype, dimensions):
|
|||||||
|
|
||||||
for dimension in dimensions:
|
for dimension in dimensions:
|
||||||
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
|
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
|
||||||
children = frappe.get_all(
|
children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
|
||||||
doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft"
|
|
||||||
)
|
|
||||||
all_dimensions += [c.name for c in children]
|
all_dimensions += [c.name for c in children]
|
||||||
|
|
||||||
return all_dimensions
|
return all_dimensions
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ class TestAccountingDimension(unittest.TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
disable_dimension()
|
disable_dimension()
|
||||||
|
frappe.flags.accounting_dimensions_details = None
|
||||||
|
frappe.flags.dimension_filter_map = None
|
||||||
|
|
||||||
|
|
||||||
def create_dimension():
|
def create_dimension():
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Accounting Dimension Filter', {
|
frappe.ui.form.on("Accounting Dimension Filter", {
|
||||||
refresh: function(frm, cdt, cdn) {
|
refresh: function (frm, cdt, cdn) {
|
||||||
let help_content =
|
let help_content = `<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
|
||||||
`<table class="table table-bordered" style="background-color: var(--scrollbar-track-color);">
|
|
||||||
<tr><td>
|
<tr><td>
|
||||||
<p>
|
<p>
|
||||||
<i class="fa fa-hand-right"></i>
|
<i class="fa fa-hand-right"></i>
|
||||||
@@ -13,67 +12,70 @@ frappe.ui.form.on('Accounting Dimension Filter', {
|
|||||||
</td></tr>
|
</td></tr>
|
||||||
</table>`;
|
</table>`;
|
||||||
|
|
||||||
frm.set_df_property('dimension_filter_help', 'options', help_content);
|
frm.set_df_property("dimension_filter_help", "options", help_content);
|
||||||
},
|
},
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
frm.set_query('applicable_on_account', 'accounts', function() {
|
frm.set_query("applicable_on_account", "accounts", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'company': frm.doc.company
|
company: frm.doc.company,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.db.get_list('Accounting Dimension',
|
frappe.db.get_list("Accounting Dimension", { fields: ["document_type"] }).then((res) => {
|
||||||
{fields: ['document_type']}).then((res) => {
|
let options = ["Cost Center", "Project"];
|
||||||
let options = ['Cost Center', 'Project'];
|
|
||||||
|
|
||||||
res.forEach((dimension) => {
|
res.forEach((dimension) => {
|
||||||
options.push(dimension.document_type);
|
options.push(dimension.document_type);
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_df_property('accounting_dimension', 'options', options);
|
frm.set_df_property("accounting_dimension", "options", options);
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.trigger('setup_filters');
|
frm.trigger("setup_filters");
|
||||||
},
|
},
|
||||||
|
|
||||||
setup_filters: function(frm) {
|
setup_filters: function (frm) {
|
||||||
let filters = {};
|
let filters = {};
|
||||||
|
|
||||||
if (frm.doc.accounting_dimension) {
|
if (frm.doc.accounting_dimension) {
|
||||||
frappe.model.with_doctype(frm.doc.accounting_dimension, function() {
|
frappe.model.with_doctype(frm.doc.accounting_dimension, function () {
|
||||||
if (frappe.model.is_tree(frm.doc.accounting_dimension)) {
|
if (frappe.model.is_tree(frm.doc.accounting_dimension)) {
|
||||||
filters['is_group'] = 0;
|
filters["is_group"] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frappe.meta.has_field(frm.doc.accounting_dimension, 'company')) {
|
if (frappe.meta.has_field(frm.doc.accounting_dimension, "company")) {
|
||||||
filters['company'] = frm.doc.company;
|
filters["company"] = frm.doc.company;
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.set_query('dimension_value', 'dimensions', function() {
|
frm.set_query("dimension_value", "dimensions", function () {
|
||||||
return {
|
return {
|
||||||
filters: filters
|
filters: filters,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
accounting_dimension: function(frm) {
|
accounting_dimension: function (frm) {
|
||||||
frm.clear_table("dimensions");
|
frm.clear_table("dimensions");
|
||||||
let row = frm.add_child("dimensions");
|
let row = frm.add_child("dimensions");
|
||||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||||
frm.fields_dict["dimensions"].grid.update_docfield_property("dimension_value", "label", frm.doc.accounting_dimension);
|
frm.fields_dict["dimensions"].grid.update_docfield_property(
|
||||||
|
"dimension_value",
|
||||||
|
"label",
|
||||||
|
frm.doc.accounting_dimension
|
||||||
|
);
|
||||||
frm.refresh_field("dimensions");
|
frm.refresh_field("dimensions");
|
||||||
frm.trigger('setup_filters');
|
frm.trigger("setup_filters");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on('Allowed Dimension', {
|
frappe.ui.form.on("Allowed Dimension", {
|
||||||
dimensions_add: function(frm, cdt, cdn) {
|
dimensions_add: function (frm, cdt, cdn) {
|
||||||
let row = locals[cdt][cdn];
|
let row = locals[cdt][cdn];
|
||||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||||
frm.refresh_field("dimensions");
|
frm.refresh_field("dimensions");
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class AccountingDimensionFilter(Document):
|
|||||||
|
|
||||||
|
|
||||||
def get_dimension_filter_map():
|
def get_dimension_filter_map():
|
||||||
|
if not frappe.flags.get("dimension_filter_map"):
|
||||||
|
# nosemgrep
|
||||||
filters = frappe.db.sql(
|
filters = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
@@ -68,7 +70,9 @@ def get_dimension_filter_map():
|
|||||||
f.is_mandatory,
|
f.is_mandatory,
|
||||||
)
|
)
|
||||||
|
|
||||||
return dimension_filter_map
|
frappe.flags.dimension_filter_map = dimension_filter_map
|
||||||
|
|
||||||
|
return frappe.flags.dimension_filter_map
|
||||||
|
|
||||||
|
|
||||||
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
|
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ class TestAccountingDimensionFilter(unittest.TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
disable_dimension_filter()
|
disable_dimension_filter()
|
||||||
disable_dimension()
|
disable_dimension()
|
||||||
|
frappe.flags.accounting_dimensions_details = None
|
||||||
|
frappe.flags.dimension_filter_map = None
|
||||||
|
|
||||||
for si in self.invoice_list:
|
for si in self.invoice_list:
|
||||||
si.load_from_db()
|
si.load_from_db()
|
||||||
@@ -55,9 +57,7 @@ class TestAccountingDimensionFilter(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def create_accounting_dimension_filter():
|
def create_accounting_dimension_filter():
|
||||||
if not frappe.db.get_value(
|
if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}):
|
||||||
"Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
|
|
||||||
):
|
|
||||||
frappe.get_doc(
|
frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Accounting Dimension Filter",
|
"doctype": "Accounting Dimension Filter",
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Accounting Period', {
|
frappe.ui.form.on("Accounting Period", {
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
if(frm.doc.closed_documents.length === 0 || (frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)) {
|
if (
|
||||||
|
frm.doc.closed_documents.length === 0 ||
|
||||||
|
(frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)
|
||||||
|
) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "get_doctypes_for_closing",
|
method: "get_doctypes_for_closing",
|
||||||
doc:frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if(r.message) {
|
if (r.message) {
|
||||||
cur_frm.clear_table("closed_documents");
|
cur_frm.clear_table("closed_documents");
|
||||||
r.message.forEach(function(element) {
|
r.message.forEach(function (element) {
|
||||||
var c = frm.add_child("closed_documents");
|
var c = frm.add_child("closed_documents");
|
||||||
c.document_type = element.document_type;
|
c.document_type = element.document_type;
|
||||||
c.closed = element.closed;
|
c.closed = element.closed;
|
||||||
});
|
});
|
||||||
refresh_field("closed_documents");
|
refresh_field("closed_documents");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.set_query("document_type", "closed_documents", () => {
|
frm.set_query("document_type", "closed_documents", () => {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_doctypes_for_closing",
|
query: "erpnext.controllers.queries.get_doctypes_for_closing",
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ class AccountingPeriod(Document):
|
|||||||
for doctype_for_closing in self.get_doctypes_for_closing():
|
for doctype_for_closing in self.get_doctypes_for_closing():
|
||||||
self.append(
|
self.append(
|
||||||
"closed_documents",
|
"closed_documents",
|
||||||
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
|
{
|
||||||
|
"document_type": doctype_for_closing.document_type,
|
||||||
|
"closed": doctype_for_closing.closed,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ class TestAccountingPeriod(unittest.TestCase):
|
|||||||
ap1 = create_accounting_period(period_name="Test Accounting Period 2")
|
ap1 = create_accounting_period(period_name="Test Accounting Period 2")
|
||||||
ap1.save()
|
ap1.save()
|
||||||
|
|
||||||
doc = create_sales_invoice(
|
doc = create_sales_invoice(do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
|
||||||
do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
|
|
||||||
)
|
|
||||||
self.assertRaises(ClosedAccountingPeriod, doc.save)
|
self.assertRaises(ClosedAccountingPeriod, doc.save)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Accounts Settings', {
|
frappe.ui.form.on("Accounts Settings", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {},
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
frappe.ui.form.on("Accounts Settings", {
|
||||||
frappe.ui.form.on('Accounts Settings', {
|
refresh: function (frm) {
|
||||||
refresh: function(frm) {
|
|
||||||
frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through");
|
frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through");
|
||||||
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
|
frm.set_df_property(
|
||||||
|
"frozen_accounts_modifier",
|
||||||
|
"label",
|
||||||
|
"Role Allowed to Close Books & Make Changes to Closed Periods"
|
||||||
|
);
|
||||||
frm.set_df_property("credit_controller", "label", "Credit Manager");
|
frm.set_df_property("credit_controller", "label", "Credit Manager");
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-01 12:32:34.044911",
|
"modified": "2024-01-03 11:13:02.669632",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Allowed To Transact With",
|
"name": "Allowed To Transact With",
|
||||||
@@ -28,5 +29,6 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -1,41 +1,39 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
frappe.provide('erpnext.integrations');
|
frappe.provide("erpnext.integrations");
|
||||||
|
|
||||||
frappe.ui.form.on('Bank', {
|
frappe.ui.form.on("Bank", {
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
add_fields_to_mapping_table(frm);
|
add_fields_to_mapping_table(frm);
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
add_fields_to_mapping_table(frm);
|
add_fields_to_mapping_table(frm);
|
||||||
|
|
||||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank' };
|
frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Bank" };
|
||||||
|
|
||||||
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
|
frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal);
|
||||||
|
|
||||||
if (frm.doc.__islocal) {
|
if (frm.doc.__islocal) {
|
||||||
frm.set_df_property('address_and_contact', 'hidden', 1);
|
frm.set_df_property("address_and_contact", "hidden", 1);
|
||||||
frappe.contacts.clear_address_and_contact(frm);
|
frappe.contacts.clear_address_and_contact(frm);
|
||||||
}
|
} else {
|
||||||
else {
|
frm.set_df_property("address_and_contact", "hidden", 0);
|
||||||
frm.set_df_property('address_and_contact', 'hidden', 0);
|
|
||||||
frappe.contacts.render_address_and_contact(frm);
|
frappe.contacts.render_address_and_contact(frm);
|
||||||
}
|
}
|
||||||
if (frm.doc.plaid_access_token) {
|
if (frm.doc.plaid_access_token) {
|
||||||
frm.add_custom_button(__('Refresh Plaid Link'), () => {
|
frm.add_custom_button(__("Refresh Plaid Link"), () => {
|
||||||
new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
|
new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
let add_fields_to_mapping_table = function (frm) {
|
let add_fields_to_mapping_table = function (frm) {
|
||||||
let options = [];
|
let options = [];
|
||||||
|
|
||||||
frappe.model.with_doctype("Bank Transaction", function() {
|
frappe.model.with_doctype("Bank Transaction", function () {
|
||||||
let meta = frappe.get_meta("Bank Transaction");
|
let meta = frappe.get_meta("Bank Transaction");
|
||||||
meta.fields.forEach(value => {
|
meta.fields.forEach((value) => {
|
||||||
if (!["Section Break", "Column Break"].includes(value.fieldtype)) {
|
if (!["Section Break", "Column Break"].includes(value.fieldtype)) {
|
||||||
options.push(value.fieldname);
|
options.push(value.fieldname);
|
||||||
}
|
}
|
||||||
@@ -43,30 +41,32 @@ let add_fields_to_mapping_table = function (frm) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
|
frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
|
||||||
'bank_transaction_field', 'options', options
|
"bank_transaction_field",
|
||||||
|
"options",
|
||||||
|
options
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||||
constructor(access_token) {
|
constructor(access_token) {
|
||||||
this.access_token = access_token;
|
this.access_token = access_token;
|
||||||
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
this.plaidUrl = "https://cdn.plaid.com/link/v2/stable/link-initialize.js";
|
||||||
this.init_config();
|
this.init_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
async init_config() {
|
async init_config() {
|
||||||
this.plaid_env = await frappe.db.get_single_value('Plaid Settings', 'plaid_env');
|
this.plaid_env = await frappe.db.get_single_value("Plaid Settings", "plaid_env");
|
||||||
this.token = await this.get_link_token_for_update();
|
this.token = await this.get_link_token_for_update();
|
||||||
this.init_plaid();
|
this.init_plaid();
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_link_token_for_update() {
|
async get_link_token_for_update() {
|
||||||
const token = frappe.xcall(
|
const token = frappe.xcall(
|
||||||
'erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update',
|
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update",
|
||||||
{ access_token: this.access_token }
|
{ access_token: this.access_token }
|
||||||
)
|
);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
frappe.throw(__('Cannot retrieve link token for update. Check Error Log for more information'));
|
frappe.throw(__("Cannot retrieve link token for update. Check Error Log for more information"));
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -93,13 +93,13 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
|||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const el = document.createElement('script');
|
const el = document.createElement("script");
|
||||||
el.type = 'text/javascript';
|
el.type = "text/javascript";
|
||||||
el.async = true;
|
el.async = true;
|
||||||
el.src = src;
|
el.src = src;
|
||||||
el.addEventListener('load', resolve);
|
el.addEventListener("load", resolve);
|
||||||
el.addEventListener('error', reject);
|
el.addEventListener("error", reject);
|
||||||
el.addEventListener('abort', reject);
|
el.addEventListener("abort", reject);
|
||||||
document.head.appendChild(el);
|
document.head.appendChild(el);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -108,20 +108,29 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
|||||||
me.linkHandler = Plaid.create({
|
me.linkHandler = Plaid.create({
|
||||||
env: me.plaid_env,
|
env: me.plaid_env,
|
||||||
token: me.token,
|
token: me.token,
|
||||||
onSuccess: me.plaid_success
|
onSuccess: me.plaid_success,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onScriptError(error) {
|
onScriptError(error) {
|
||||||
frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
|
frappe.msgprint(
|
||||||
|
__(
|
||||||
|
"There was an issue connecting to Plaid's authentication server. Check browser console for more information"
|
||||||
|
)
|
||||||
|
);
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
plaid_success(token, response) {
|
plaid_success(token, response) {
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids', {
|
frappe
|
||||||
|
.xcall(
|
||||||
|
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids",
|
||||||
|
{
|
||||||
response: response,
|
response: response,
|
||||||
}).then(() => {
|
}
|
||||||
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
|
)
|
||||||
|
.then(() => {
|
||||||
|
frappe.show_alert({ message: __("Plaid Link Updated"), indicator: "green" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,45 +1,49 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Bank Account', {
|
frappe.ui.form.on("Bank Account", {
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("account", function() {
|
frm.set_query("account", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'account_type': 'Bank',
|
account_type: "Bank",
|
||||||
'company': frm.doc.company,
|
company: frm.doc.company,
|
||||||
'is_group': 0
|
is_group: 0,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
frm.set_query("party_type", function() {
|
frm.set_query("party_type", function () {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
|
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank Account' }
|
frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Bank Account" };
|
||||||
|
|
||||||
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
|
frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal);
|
||||||
|
|
||||||
if (frm.doc.__islocal) {
|
if (frm.doc.__islocal) {
|
||||||
frappe.contacts.clear_address_and_contact(frm);
|
frappe.contacts.clear_address_and_contact(frm);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
frappe.contacts.render_address_and_contact(frm);
|
frappe.contacts.render_address_and_contact(frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.integration_id) {
|
if (frm.doc.integration_id) {
|
||||||
frm.add_custom_button(__("Unlink external integrations"), function() {
|
frm.add_custom_button(__("Unlink external integrations"), function () {
|
||||||
frappe.confirm(__("This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"), function() {
|
frappe.confirm(
|
||||||
|
__(
|
||||||
|
"This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"
|
||||||
|
),
|
||||||
|
function () {
|
||||||
frm.set_value("integration_id", "");
|
frm.set_value("integration_id", "");
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
is_company_account: function(frm) {
|
is_company_account: function (frm) {
|
||||||
frm.set_df_property('account', 'reqd', frm.doc.is_company_account);
|
frm.set_df_property("account", "reqd", frm.doc.is_company_account);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"account_type",
|
"account_type",
|
||||||
"account_subtype",
|
"account_subtype",
|
||||||
"column_break_7",
|
"column_break_7",
|
||||||
|
"disabled",
|
||||||
"is_default",
|
"is_default",
|
||||||
"is_company_account",
|
"is_company_account",
|
||||||
"company",
|
"company",
|
||||||
@@ -199,10 +200,16 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"label": "Branch Code"
|
"label": "Branch Code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Disabled"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-05-04 15:49:42.620630",
|
"modified": "2024-02-02 17:50:09.768835",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Account",
|
"name": "Bank Account",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from frappe.contacts.address_and_contact import (
|
|||||||
load_address_and_contact,
|
load_address_and_contact,
|
||||||
)
|
)
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import comma_and, get_link_to_form
|
||||||
|
|
||||||
|
|
||||||
class BankAccount(Document):
|
class BankAccount(Document):
|
||||||
@@ -25,6 +26,19 @@ class BankAccount(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_company()
|
self.validate_company()
|
||||||
self.validate_iban()
|
self.validate_iban()
|
||||||
|
self.validate_account()
|
||||||
|
|
||||||
|
def validate_account(self):
|
||||||
|
if self.account:
|
||||||
|
if accounts := frappe.db.get_all(
|
||||||
|
"Bank Account", filters={"account": self.account, "name": ["!=", self.name]}, as_list=1
|
||||||
|
):
|
||||||
|
frappe.throw(
|
||||||
|
_("'{0}' account is already used by {1}. Use another account.").format(
|
||||||
|
frappe.bold(self.account),
|
||||||
|
frappe.bold(comma_and([get_link_to_form(self.doctype, x[0]) for x in accounts])),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def validate_company(self):
|
def validate_company(self):
|
||||||
if self.is_company_account and not self.company:
|
if self.is_company_account and not self.company:
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ class TestBankAccount(unittest.TestCase):
|
|||||||
try:
|
try:
|
||||||
bank_account.validate_iban()
|
bank_account.validate_iban()
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
msg = "BankAccount.validate_iban() failed for valid IBAN {}".format(iban)
|
msg = f"BankAccount.validate_iban() failed for valid IBAN {iban}"
|
||||||
self.fail(msg=msg)
|
self.fail(msg=msg)
|
||||||
|
|
||||||
for not_iban in invalid_ibans:
|
for not_iban in invalid_ibans:
|
||||||
bank_account.iban = not_iban
|
bank_account.iban = not_iban
|
||||||
msg = "BankAccount.validate_iban() accepted invalid IBAN {}".format(not_iban)
|
msg = f"BankAccount.validate_iban() accepted invalid IBAN {not_iban}"
|
||||||
with self.assertRaises(ValidationError, msg=msg):
|
with self.assertRaises(ValidationError, msg=msg):
|
||||||
bank_account.validate_iban()
|
bank_account.validate_iban()
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Bank Account Subtype', {
|
frappe.ui.form.on("Bank Account Subtype", {
|
||||||
refresh: function() {
|
refresh: function () {},
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Bank Account Type', {
|
frappe.ui.form.on("Bank Account Type", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,80 +2,76 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("Bank Clearance", {
|
frappe.ui.form.on("Bank Clearance", {
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
frm.add_fetch("account", "account_currency", "account_currency");
|
frm.add_fetch("account", "account_currency", "account_currency");
|
||||||
|
|
||||||
frm.set_query("account", function() {
|
frm.set_query("account", function () {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
filters: {
|
||||||
"account_type": ["in",["Bank","Cash"]],
|
account_type: ["in", ["Bank", "Cash"]],
|
||||||
"is_group": 0,
|
is_group: 0,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("bank_account", function () {
|
frm.set_query("bank_account", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'is_company_account': 1
|
is_company_account: 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
|
let default_bank_account = frappe.defaults.get_user_default("Company")
|
||||||
let default_bank_account = frappe.defaults.get_user_default("Company")?
|
? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]
|
||||||
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
: "";
|
||||||
frm.set_value("account", default_bank_account);
|
frm.set_value("account", default_bank_account);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
frm.set_value("from_date", frappe.datetime.month_start());
|
frm.set_value("from_date", frappe.datetime.month_start());
|
||||||
frm.set_value("to_date", frappe.datetime.month_end());
|
frm.set_value("to_date", frappe.datetime.month_end());
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frm.disable_save();
|
frm.disable_save();
|
||||||
frm.add_custom_button(__('Get Payment Entries'), () =>
|
frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries"));
|
||||||
frm.trigger("get_payment_entries")
|
|
||||||
);
|
|
||||||
|
|
||||||
frm.change_custom_button_type('Get Payment Entries', null, 'primary');
|
frm.change_custom_button_type("Get Payment Entries", null, "primary");
|
||||||
},
|
},
|
||||||
|
|
||||||
update_clearance_date: function(frm) {
|
update_clearance_date: function (frm) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "update_clearance_date",
|
method: "update_clearance_date",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r, rt) {
|
callback: function (r, rt) {
|
||||||
frm.refresh_field("payment_entries");
|
frm.refresh_field("payment_entries");
|
||||||
frm.refresh_fields();
|
frm.refresh_fields();
|
||||||
|
|
||||||
if (!frm.doc.payment_entries.length) {
|
if (!frm.doc.payment_entries.length) {
|
||||||
frm.change_custom_button_type('Get Payment Entries', null, 'primary');
|
frm.change_custom_button_type("Get Payment Entries", null, "primary");
|
||||||
frm.change_custom_button_type('Update Clearance Date', null, 'default');
|
frm.change_custom_button_type("Update Clearance Date", null, "default");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
get_payment_entries: function(frm) {
|
get_payment_entries: function (frm) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "get_payment_entries",
|
method: "get_payment_entries",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r, rt) {
|
callback: function (r, rt) {
|
||||||
frm.refresh_field("payment_entries");
|
frm.refresh_field("payment_entries");
|
||||||
|
|
||||||
if (frm.doc.payment_entries.length) {
|
if (frm.doc.payment_entries.length) {
|
||||||
frm.add_custom_button(__('Update Clearance Date'), () =>
|
frm.add_custom_button(__("Update Clearance Date"), () =>
|
||||||
frm.trigger("update_clearance_date")
|
frm.trigger("update_clearance_date")
|
||||||
);
|
);
|
||||||
|
|
||||||
frm.change_custom_button_type('Get Payment Entries', null, 'default');
|
frm.change_custom_button_type("Get Payment Entries", null, "default");
|
||||||
frm.change_custom_button_type('Update Clearance Date', null, 'primary');
|
frm.change_custom_button_type("Update Clearance Date", null, "primary");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class BankClearance(Document):
|
|||||||
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
|
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
|
||||||
|
|
||||||
journal_entries = frappe.db.sql(
|
journal_entries = frappe.db.sql(
|
||||||
"""
|
f"""
|
||||||
select
|
select
|
||||||
"Journal Entry" as payment_document, t1.name as payment_entry,
|
"Journal Entry" as payment_document, t1.name as payment_entry,
|
||||||
t1.cheque_no as cheque_number, t1.cheque_date,
|
t1.cheque_no as cheque_number, t1.cheque_date,
|
||||||
@@ -41,9 +41,7 @@ class BankClearance(Document):
|
|||||||
and ifnull(t1.is_opening, 'No') = 'No' {condition}
|
and ifnull(t1.is_opening, 'No') = 'No' {condition}
|
||||||
group by t2.account, t1.name
|
group by t2.account, t1.name
|
||||||
order by t1.posting_date ASC, t1.name DESC
|
order by t1.posting_date ASC, t1.name DESC
|
||||||
""".format(
|
""",
|
||||||
condition=condition
|
|
||||||
),
|
|
||||||
{"account": self.account, "from": self.from_date, "to": self.to_date},
|
{"account": self.account, "from": self.from_date, "to": self.to_date},
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
@@ -52,7 +50,7 @@ class BankClearance(Document):
|
|||||||
condition += "and bank_account = %(bank_account)s"
|
condition += "and bank_account = %(bank_account)s"
|
||||||
|
|
||||||
payment_entries = frappe.db.sql(
|
payment_entries = frappe.db.sql(
|
||||||
"""
|
f"""
|
||||||
select
|
select
|
||||||
"Payment Entry" as payment_document, name as payment_entry,
|
"Payment Entry" as payment_document, name as payment_entry,
|
||||||
reference_no as cheque_number, reference_date as cheque_date,
|
reference_no as cheque_number, reference_date as cheque_date,
|
||||||
@@ -67,9 +65,7 @@ class BankClearance(Document):
|
|||||||
{condition}
|
{condition}
|
||||||
order by
|
order by
|
||||||
posting_date ASC, name DESC
|
posting_date ASC, name DESC
|
||||||
""".format(
|
""",
|
||||||
condition=condition
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
"account": self.account,
|
"account": self.account,
|
||||||
"from": self.from_date,
|
"from": self.from_date,
|
||||||
@@ -132,11 +128,9 @@ class BankClearance(Document):
|
|||||||
query = query.where(loan_repayment.clearance_date.isnull())
|
query = query.where(loan_repayment.clearance_date.isnull())
|
||||||
|
|
||||||
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||||
query = query.where((loan_repayment.repay_from_salary == 0))
|
query = query.where(loan_repayment.repay_from_salary == 0)
|
||||||
|
|
||||||
query = query.orderby(loan_repayment.posting_date).orderby(
|
query = query.orderby(loan_repayment.posting_date).orderby(loan_repayment.name, order=frappe.qb.desc)
|
||||||
loan_repayment.name, order=frappe.qb.desc
|
|
||||||
)
|
|
||||||
|
|
||||||
loan_repayments = query.run(as_dict=True)
|
loan_repayments = query.run(as_dict=True)
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
cur_frm.add_fetch('bank_account','account','account');
|
cur_frm.add_fetch("bank_account", "account", "account");
|
||||||
cur_frm.add_fetch('bank_account','bank_account_no','bank_account_no');
|
cur_frm.add_fetch("bank_account", "bank_account_no", "bank_account_no");
|
||||||
cur_frm.add_fetch('bank_account','iban','iban');
|
cur_frm.add_fetch("bank_account", "iban", "iban");
|
||||||
cur_frm.add_fetch('bank_account','branch_code','branch_code');
|
cur_frm.add_fetch("bank_account", "branch_code", "branch_code");
|
||||||
cur_frm.add_fetch('bank','swift_number','swift_number');
|
cur_frm.add_fetch("bank", "swift_number", "swift_number");
|
||||||
|
|
||||||
frappe.ui.form.on('Bank Guarantee', {
|
frappe.ui.form.on("Bank Guarantee", {
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("bank", function() {
|
frm.set_query("bank", function () {
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
company: frm.doc.company
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
frm.set_query("bank_account", function() {
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
bank: frm.doc.bank
|
},
|
||||||
}
|
};
|
||||||
}
|
|
||||||
});
|
});
|
||||||
frm.set_query("project", function() {
|
frm.set_query("bank_account", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
customer: frm.doc.customer
|
company: frm.doc.company,
|
||||||
}
|
bank: frm.doc.bank,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
frm.set_query("project", function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
customer: frm.doc.customer,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
bg_type: function(frm) {
|
bg_type: function (frm) {
|
||||||
if (frm.doc.bg_type == "Receiving") {
|
if (frm.doc.bg_type == "Receiving") {
|
||||||
frm.set_value("reference_doctype", "Sales Order");
|
frm.set_value("reference_doctype", "Sales Order");
|
||||||
} else if (frm.doc.bg_type == "Providing") {
|
} else if (frm.doc.bg_type == "Providing") {
|
||||||
@@ -41,34 +41,33 @@ frappe.ui.form.on('Bank Guarantee', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reference_docname: function(frm) {
|
reference_docname: function (frm) {
|
||||||
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
||||||
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details",
|
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details",
|
||||||
args: {
|
args: {
|
||||||
"bank_guarantee_type": frm.doc.bg_type,
|
bank_guarantee_type: frm.doc.bg_type,
|
||||||
"reference_name": frm.doc.reference_docname
|
reference_name: frm.doc.reference_docname,
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
if (r.message[party_field]) frm.set_value(party_field, r.message[party_field]);
|
if (r.message[party_field]) frm.set_value(party_field, r.message[party_field]);
|
||||||
if (r.message.project) frm.set_value("project", r.message.project);
|
if (r.message.project) frm.set_value("project", r.message.project);
|
||||||
if (r.message.grand_total) frm.set_value("amount", r.message.grand_total);
|
if (r.message.grand_total) frm.set_value("amount", r.message.grand_total);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
start_date: function(frm) {
|
start_date: function (frm) {
|
||||||
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
|
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
|
||||||
cur_frm.set_value("end_date", end_date);
|
cur_frm.set_value("end_date", end_date);
|
||||||
},
|
},
|
||||||
validity: function(frm) {
|
validity: function (frm) {
|
||||||
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
|
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
|
||||||
cur_frm.set_value("end_date", end_date);
|
cur_frm.set_value("end_date", end_date);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,21 +8,22 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
'is_company_account': 1
|
is_company_account: 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
let no_bank_transactions_text =
|
let no_bank_transactions_text = `<div class="text-muted text-center">${__(
|
||||||
`<div class="text-muted text-center">${__("No Matching Bank Transactions Found")}</div>`
|
"No Matching Bank Transactions Found"
|
||||||
|
)}</div>`;
|
||||||
set_field_options("no_bank_transactions", no_bank_transactions_text);
|
set_field_options("no_bank_transactions", no_bank_transactions_text);
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
// Set default filter dates
|
// Set default filter dates
|
||||||
let today = frappe.datetime.get_today()
|
let today = frappe.datetime.get_today();
|
||||||
frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1);
|
frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1);
|
||||||
frm.doc.bank_statement_to_date = today;
|
frm.doc.bank_statement_to_date = today;
|
||||||
frm.trigger('bank_account');
|
frm.trigger("bank_account");
|
||||||
},
|
},
|
||||||
|
|
||||||
filter_by_reference_date: function (frm) {
|
filter_by_reference_date: function (frm) {
|
||||||
@@ -37,14 +38,11 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
frm.disable_save();
|
frm.disable_save();
|
||||||
frappe.require("bank-reconciliation-tool.bundle.js", () =>
|
frappe.require("bank-reconciliation-tool.bundle.js", () => frm.trigger("make_reconciliation_tool"));
|
||||||
frm.trigger("make_reconciliation_tool")
|
|
||||||
);
|
|
||||||
|
|
||||||
frm.add_custom_button(__("Upload Bank Statement"), () =>
|
frm.add_custom_button(__("Upload Bank Statement"), () =>
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:
|
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
||||||
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
|
||||||
args: {
|
args: {
|
||||||
dt: frm.doc.doctype,
|
dt: frm.doc.doctype,
|
||||||
dn: frm.doc.name,
|
dn: frm.doc.name,
|
||||||
@@ -54,17 +52,13 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
callback: function (r) {
|
callback: function (r) {
|
||||||
if (!r.exc) {
|
if (!r.exc) {
|
||||||
var doc = frappe.model.sync(r.message);
|
var doc = frappe.model.sync(r.message);
|
||||||
frappe.set_route(
|
frappe.set_route("Form", doc[0].doctype, doc[0].name);
|
||||||
"Form",
|
|
||||||
doc[0].doctype,
|
|
||||||
doc[0].name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
frm.add_custom_button(__('Auto Reconcile'), function() {
|
frm.add_custom_button(__("Auto Reconcile"), function () {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
|
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
|
||||||
args: {
|
args: {
|
||||||
@@ -75,33 +69,22 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
from_reference_date: frm.doc.from_reference_date,
|
from_reference_date: frm.doc.from_reference_date,
|
||||||
to_reference_date: frm.doc.to_reference_date,
|
to_reference_date: frm.doc.to_reference_date,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.add_custom_button(__('Get Unreconciled Entries'), function() {
|
frm.add_custom_button(__("Get Unreconciled Entries"), function () {
|
||||||
frm.trigger("make_reconciliation_tool");
|
frm.trigger("make_reconciliation_tool");
|
||||||
});
|
});
|
||||||
frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary');
|
frm.change_custom_button_type("Get Unreconciled Entries", null, "primary");
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
bank_account: function (frm) {
|
bank_account: function (frm) {
|
||||||
frappe.db.get_value(
|
frappe.db.get_value("Bank Account", frm.doc.bank_account, "account", (r) => {
|
||||||
"Bank Account",
|
frappe.db.get_value("Account", r.account, "account_currency", (r) => {
|
||||||
frm.doc.bank_account,
|
|
||||||
"account",
|
|
||||||
(r) => {
|
|
||||||
frappe.db.get_value(
|
|
||||||
"Account",
|
|
||||||
r.account,
|
|
||||||
"account_currency",
|
|
||||||
(r) => {
|
|
||||||
frm.doc.account_currency = r.account_currency;
|
frm.doc.account_currency = r.account_currency;
|
||||||
frm.trigger("render_chart");
|
frm.trigger("render_chart");
|
||||||
}
|
});
|
||||||
);
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
frm.trigger("get_account_opening_balance");
|
frm.trigger("get_account_opening_balance");
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -120,11 +103,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
) {
|
) {
|
||||||
frm.trigger("render_chart");
|
frm.trigger("render_chart");
|
||||||
frm.trigger("render");
|
frm.trigger("render");
|
||||||
frappe.utils.scroll_to(
|
frappe.utils.scroll_to(frm.get_field("reconciliation_tool_cards").$wrapper, true, 30);
|
||||||
frm.get_field("reconciliation_tool_cards").$wrapper,
|
|
||||||
true,
|
|
||||||
30
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -133,11 +112,10 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
get_account_opening_balance(frm) {
|
get_account_opening_balance(frm) {
|
||||||
if (frm.doc.bank_account && frm.doc.bank_statement_from_date) {
|
if (frm.doc.bank_account && frm.doc.bank_statement_from_date) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:
|
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
||||||
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
|
||||||
args: {
|
args: {
|
||||||
bank_account: frm.doc.bank_account,
|
bank_account: frm.doc.bank_account,
|
||||||
till_date: frm.doc.bank_statement_from_date,
|
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1),
|
||||||
},
|
},
|
||||||
callback: (response) => {
|
callback: (response) => {
|
||||||
frm.set_value("account_opening_balance", response.message);
|
frm.set_value("account_opening_balance", response.message);
|
||||||
@@ -149,8 +127,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
get_cleared_balance(frm) {
|
get_cleared_balance(frm) {
|
||||||
if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
|
if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method:
|
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
||||||
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
|
|
||||||
args: {
|
args: {
|
||||||
bank_account: frm.doc.bank_account,
|
bank_account: frm.doc.bank_account,
|
||||||
till_date: frm.doc.bank_statement_to_date,
|
till_date: frm.doc.bank_statement_to_date,
|
||||||
@@ -163,41 +140,30 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
render_chart(frm) {
|
render_chart(frm) {
|
||||||
frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager(
|
frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager({
|
||||||
{
|
$reconciliation_tool_cards: frm.get_field("reconciliation_tool_cards").$wrapper,
|
||||||
$reconciliation_tool_cards: frm.get_field(
|
bank_statement_closing_balance: frm.doc.bank_statement_closing_balance,
|
||||||
"reconciliation_tool_cards"
|
|
||||||
).$wrapper,
|
|
||||||
bank_statement_closing_balance:
|
|
||||||
frm.doc.bank_statement_closing_balance,
|
|
||||||
cleared_balance: frm.cleared_balance,
|
cleared_balance: frm.cleared_balance,
|
||||||
currency: frm.doc.account_currency,
|
currency: frm.doc.account_currency,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render(frm) {
|
render(frm) {
|
||||||
if (frm.doc.bank_account) {
|
if (frm.doc.bank_account) {
|
||||||
frm.bank_reconciliation_data_table_manager = new erpnext.accounts.bank_reconciliation.DataTableManager(
|
frm.bank_reconciliation_data_table_manager =
|
||||||
{
|
new erpnext.accounts.bank_reconciliation.DataTableManager({
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
bank_account: frm.doc.bank_account,
|
bank_account: frm.doc.bank_account,
|
||||||
$reconciliation_tool_dt: frm.get_field(
|
$reconciliation_tool_dt: frm.get_field("reconciliation_tool_dt").$wrapper,
|
||||||
"reconciliation_tool_dt"
|
$no_bank_transactions: frm.get_field("no_bank_transactions").$wrapper,
|
||||||
).$wrapper,
|
|
||||||
$no_bank_transactions: frm.get_field(
|
|
||||||
"no_bank_transactions"
|
|
||||||
).$wrapper,
|
|
||||||
bank_statement_from_date: frm.doc.bank_statement_from_date,
|
bank_statement_from_date: frm.doc.bank_statement_from_date,
|
||||||
bank_statement_to_date: frm.doc.bank_statement_to_date,
|
bank_statement_to_date: frm.doc.bank_statement_to_date,
|
||||||
filter_by_reference_date: frm.doc.filter_by_reference_date,
|
filter_by_reference_date: frm.doc.filter_by_reference_date,
|
||||||
from_reference_date: frm.doc.from_reference_date,
|
from_reference_date: frm.doc.from_reference_date,
|
||||||
to_reference_date: frm.doc.to_reference_date,
|
to_reference_date: frm.doc.to_reference_date,
|
||||||
bank_statement_closing_balance:
|
bank_statement_closing_balance: frm.doc.bank_statement_closing_balance,
|
||||||
frm.doc.bank_statement_closing_balance,
|
|
||||||
cards_manager: frm.cards_manager,
|
cards_manager: frm.cards_manager,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,9 +61,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
|||||||
def get_account_balance(bank_account, till_date):
|
def get_account_balance(bank_account, till_date):
|
||||||
# returns account balance till the specified date
|
# returns account balance till the specified date
|
||||||
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
||||||
filters = frappe._dict(
|
filters = frappe._dict({"account": account, "report_date": till_date, "include_pos_transactions": 1})
|
||||||
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
|
|
||||||
)
|
|
||||||
data = get_entries(filters)
|
data = get_entries(filters)
|
||||||
|
|
||||||
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
|
||||||
@@ -76,10 +74,7 @@ def get_account_balance(bank_account, till_date):
|
|||||||
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
|
||||||
|
|
||||||
bank_bal = (
|
bank_bal = (
|
||||||
flt(balance_as_per_system)
|
flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
|
||||||
- flt(total_debit)
|
|
||||||
+ flt(total_credit)
|
|
||||||
+ amounts_not_reflected_in_system
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return bank_bal
|
return bank_bal
|
||||||
@@ -377,12 +372,13 @@ def auto_reconcile_vouchers(
|
|||||||
)
|
)
|
||||||
transaction = frappe.get_doc("Bank Transaction", transaction.name)
|
transaction = frappe.get_doc("Bank Transaction", transaction.name)
|
||||||
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||||
matched_trans = 0
|
|
||||||
for voucher in vouchers:
|
for voucher in vouchers:
|
||||||
gl_entry = frappe.db.get_value(
|
gl_entry = frappe.db.get_value(
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
dict(
|
dict(
|
||||||
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
|
account=account,
|
||||||
|
voucher_type=voucher["payment_doctype"],
|
||||||
|
voucher_no=voucher["payment_name"],
|
||||||
),
|
),
|
||||||
["credit", "debit"],
|
["credit", "debit"],
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
@@ -731,7 +727,7 @@ def get_lr_matching_query(bank_account, exact_match, filters):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||||
query = query.where((loan_repayment.repay_from_salary == 0))
|
query = query.where(loan_repayment.repay_from_salary == 0)
|
||||||
|
|
||||||
if exact_match:
|
if exact_match:
|
||||||
query.where(loan_repayment.amount_paid == filters.get("amount"))
|
query.where(loan_repayment.amount_paid == filters.get("amount"))
|
||||||
@@ -764,7 +760,7 @@ def get_pe_matching_query(
|
|||||||
if cint(filter_by_reference_date):
|
if cint(filter_by_reference_date):
|
||||||
filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
|
filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
|
||||||
order_by = " reference_date"
|
order_by = " reference_date"
|
||||||
if frappe.flags.auto_reconcile_vouchers == True:
|
if frappe.flags.auto_reconcile_vouchers is True:
|
||||||
filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
|
filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
|
||||||
return f"""
|
return f"""
|
||||||
SELECT
|
SELECT
|
||||||
@@ -815,7 +811,7 @@ def get_je_matching_query(
|
|||||||
if cint(filter_by_reference_date):
|
if cint(filter_by_reference_date):
|
||||||
filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
|
filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
|
||||||
order_by = " je.cheque_date"
|
order_by = " je.cheque_date"
|
||||||
if frappe.flags.auto_reconcile_vouchers == True:
|
if frappe.flags.auto_reconcile_vouchers is True:
|
||||||
filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'"
|
filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'"
|
||||||
return f"""
|
return f"""
|
||||||
SELECT
|
SELECT
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
frm.import_in_progress = false;
|
frm.import_in_progress = false;
|
||||||
if (data_import !== frm.doc.name) return;
|
if (data_import !== frm.doc.name) return;
|
||||||
frappe.model.clear_doc("Bank Statement Import", frm.doc.name);
|
frappe.model.clear_doc("Bank Statement Import", frm.doc.name);
|
||||||
frappe.model
|
frappe.model.with_doc("Bank Statement Import", frm.doc.name).then(() => {
|
||||||
.with_doc("Bank Statement Import", frm.doc.name)
|
|
||||||
.then(() => {
|
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -48,20 +46,9 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
: __("Updating {0} of {1}, {2}", message_args);
|
: __("Updating {0} of {1}, {2}", message_args);
|
||||||
}
|
}
|
||||||
if (data.skipping) {
|
if (data.skipping) {
|
||||||
message = __(
|
message = __("Skipping {0} of {1}, {2}", [data.current, data.total, eta_message]);
|
||||||
"Skipping {0} of {1}, {2}",
|
|
||||||
[
|
|
||||||
data.current,
|
|
||||||
data.total,
|
|
||||||
eta_message,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
frm.dashboard.show_progress(
|
frm.dashboard.show_progress(__("Import Progress"), percent, message);
|
||||||
__("Import Progress"),
|
|
||||||
percent,
|
|
||||||
message
|
|
||||||
);
|
|
||||||
frm.page.set_indicator(__("In Progress"), "orange");
|
frm.page.set_indicator(__("In Progress"), "orange");
|
||||||
|
|
||||||
// hide progress when complete
|
// hide progress when complete
|
||||||
@@ -103,15 +90,12 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
frm.trigger("show_report_error_button");
|
frm.trigger("show_report_error_button");
|
||||||
|
|
||||||
if (frm.doc.status === "Partial Success") {
|
if (frm.doc.status === "Partial Success") {
|
||||||
frm.add_custom_button(__("Export Errored Rows"), () =>
|
frm.add_custom_button(__("Export Errored Rows"), () => frm.trigger("export_errored_rows"));
|
||||||
frm.trigger("export_errored_rows")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.status.includes("Success")) {
|
if (frm.doc.status.includes("Success")) {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(__("Go to {0} List", [__(frm.doc.reference_doctype)]), () =>
|
||||||
__("Go to {0} List", [__(frm.doc.reference_doctype)]),
|
frappe.set_route("List", frm.doc.reference_doctype)
|
||||||
() => frappe.set_route("List", frm.doc.reference_doctype)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -128,13 +112,8 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
frm.disable_save();
|
frm.disable_save();
|
||||||
if (frm.doc.status !== "Success") {
|
if (frm.doc.status !== "Success") {
|
||||||
if (!frm.is_new() && frm.has_import_file()) {
|
if (!frm.is_new() && frm.has_import_file()) {
|
||||||
let label =
|
let label = frm.doc.status === "Pending" ? __("Start Import") : __("Retry");
|
||||||
frm.doc.status === "Pending"
|
frm.page.set_primary_action(label, () => frm.events.start_import(frm));
|
||||||
? __("Start Import")
|
|
||||||
: __("Retry");
|
|
||||||
frm.page.set_primary_action(label, () =>
|
|
||||||
frm.events.start_import(frm)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
frm.page.set_primary_action(__("Save"), () => frm.save());
|
frm.page.set_primary_action(__("Save"), () => frm.save());
|
||||||
}
|
}
|
||||||
@@ -236,8 +215,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
download_template() {
|
download_template() {
|
||||||
let method =
|
let method = "/api/method/frappe.core.doctype.data_import.data_import.download_template";
|
||||||
"/api/method/frappe.core.doctype.data_import.data_import.download_template";
|
|
||||||
|
|
||||||
open_url_post(method, {
|
open_url_post(method, {
|
||||||
doctype: "Bank Transaction",
|
doctype: "Bank Transaction",
|
||||||
@@ -250,7 +228,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
"description",
|
"description",
|
||||||
"reference_number",
|
"reference_number",
|
||||||
"bank_account",
|
"bank_account",
|
||||||
"currency"
|
"currency",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -321,10 +299,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
show_import_preview(frm, preview_data) {
|
show_import_preview(frm, preview_data) {
|
||||||
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
|
||||||
|
|
||||||
if (
|
if (frm.import_preview && frm.import_preview.doctype === frm.doc.reference_doctype) {
|
||||||
frm.import_preview &&
|
|
||||||
frm.import_preview.doctype === frm.doc.reference_doctype
|
|
||||||
) {
|
|
||||||
frm.import_preview.preview_data = preview_data;
|
frm.import_preview.preview_data = preview_data;
|
||||||
frm.import_preview.import_log = import_log;
|
frm.import_preview.import_log = import_log;
|
||||||
frm.import_preview.refresh();
|
frm.import_preview.refresh();
|
||||||
@@ -340,19 +315,10 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
frm,
|
frm,
|
||||||
events: {
|
events: {
|
||||||
remap_column(changed_map) {
|
remap_column(changed_map) {
|
||||||
let template_options = JSON.parse(
|
let template_options = JSON.parse(frm.doc.template_options || "{}");
|
||||||
frm.doc.template_options || "{}"
|
template_options.column_to_field_map = template_options.column_to_field_map || {};
|
||||||
);
|
Object.assign(template_options.column_to_field_map, changed_map);
|
||||||
template_options.column_to_field_map =
|
frm.set_value("template_options", JSON.stringify(template_options));
|
||||||
template_options.column_to_field_map || {};
|
|
||||||
Object.assign(
|
|
||||||
template_options.column_to_field_map,
|
|
||||||
changed_map
|
|
||||||
);
|
|
||||||
frm.set_value(
|
|
||||||
"template_options",
|
|
||||||
JSON.stringify(template_options)
|
|
||||||
);
|
|
||||||
frm.save().then(() => frm.trigger("import_file"));
|
frm.save().then(() => frm.trigger("import_file"));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -386,8 +352,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
let other_warnings = [];
|
let other_warnings = [];
|
||||||
for (let warning of warnings) {
|
for (let warning of warnings) {
|
||||||
if (warning.row) {
|
if (warning.row) {
|
||||||
warnings_by_row[warning.row] =
|
warnings_by_row[warning.row] = warnings_by_row[warning.row] || [];
|
||||||
warnings_by_row[warning.row] || [];
|
|
||||||
warnings_by_row[warning.row].push(warning);
|
warnings_by_row[warning.row].push(warning);
|
||||||
} else {
|
} else {
|
||||||
other_warnings.push(warning);
|
other_warnings.push(warning);
|
||||||
@@ -402,9 +367,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
if (w.field) {
|
if (w.field) {
|
||||||
let label =
|
let label =
|
||||||
w.field.label +
|
w.field.label +
|
||||||
(w.field.parent !== frm.doc.reference_doctype
|
(w.field.parent !== frm.doc.reference_doctype ? ` (${w.field.parent})` : "");
|
||||||
? ` (${w.field.parent})`
|
|
||||||
: "");
|
|
||||||
return `<li>${label}: ${w.message}</li>`;
|
return `<li>${label}: ${w.message}</li>`;
|
||||||
}
|
}
|
||||||
return `<li>${w.message}</li>`;
|
return `<li>${w.message}</li>`;
|
||||||
@@ -423,10 +386,9 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
.map((warning) => {
|
.map((warning) => {
|
||||||
let header = "";
|
let header = "";
|
||||||
if (warning.col) {
|
if (warning.col) {
|
||||||
let column_number = `<span class="text-uppercase">${__(
|
let column_number = `<span class="text-uppercase">${__("Column {0}", [
|
||||||
"Column {0}",
|
warning.col,
|
||||||
[warning.col]
|
])}</span>`;
|
||||||
)}</span>`;
|
|
||||||
let column_header = columns[warning.col].header_title;
|
let column_header = columns[warning.col].header_title;
|
||||||
header = `${column_number} (${column_header})`;
|
header = `${column_number} (${column_header})`;
|
||||||
}
|
}
|
||||||
@@ -465,36 +427,28 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
let html = "";
|
let html = "";
|
||||||
if (log.success) {
|
if (log.success) {
|
||||||
if (frm.doc.import_type === "Insert New Records") {
|
if (frm.doc.import_type === "Insert New Records") {
|
||||||
html = __(
|
html = __("Successfully imported {0}", [
|
||||||
"Successfully imported {0}", [
|
|
||||||
`<span class="underline">${frappe.utils.get_form_link(
|
`<span class="underline">${frappe.utils.get_form_link(
|
||||||
frm.doc.reference_doctype,
|
frm.doc.reference_doctype,
|
||||||
log.docname,
|
log.docname,
|
||||||
true
|
true
|
||||||
)}<span>`,
|
)}<span>`,
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
html = __(
|
html = __("Successfully updated {0}", [
|
||||||
"Successfully updated {0}", [
|
|
||||||
`<span class="underline">${frappe.utils.get_form_link(
|
`<span class="underline">${frappe.utils.get_form_link(
|
||||||
frm.doc.reference_doctype,
|
frm.doc.reference_doctype,
|
||||||
log.docname,
|
log.docname,
|
||||||
true
|
true
|
||||||
)}<span>`,
|
)}<span>`,
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let messages = log.messages
|
let messages = log.messages
|
||||||
.map(JSON.parse)
|
.map(JSON.parse)
|
||||||
.map((m) => {
|
.map((m) => {
|
||||||
let title = m.title
|
let title = m.title ? `<strong>${m.title}</strong>` : "";
|
||||||
? `<strong>${m.title}</strong>`
|
let message = m.message ? `<div>${m.message}</div>` : "";
|
||||||
: "";
|
|
||||||
let message = m.message
|
|
||||||
? `<div>${m.message}</div>`
|
|
||||||
: "";
|
|
||||||
return title + message;
|
return title + message;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ INVALID_VALUES = ("", None)
|
|||||||
|
|
||||||
class BankStatementImport(DataImport):
|
class BankStatementImport(DataImport):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(BankStatementImport, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
doc_before_save = self.get_doc_before_save()
|
doc_before_save = self.get_doc_before_save()
|
||||||
@@ -30,7 +30,6 @@ class BankStatementImport(DataImport):
|
|||||||
or (doc_before_save and doc_before_save.import_file != self.import_file)
|
or (doc_before_save and doc_before_save.import_file != self.import_file)
|
||||||
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
|
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
|
||||||
):
|
):
|
||||||
|
|
||||||
template_options_dict = {}
|
template_options_dict = {}
|
||||||
column_to_field_map = {}
|
column_to_field_map = {}
|
||||||
bank = frappe.get_doc("Bank", self.bank)
|
bank = frappe.get_doc("Bank", self.bank)
|
||||||
@@ -45,7 +44,6 @@ class BankStatementImport(DataImport):
|
|||||||
self.validate_google_sheets_url()
|
self.validate_google_sheets_url()
|
||||||
|
|
||||||
def start_import(self):
|
def start_import(self):
|
||||||
|
|
||||||
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
|
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
|
||||||
self.import_file, self.google_sheets_url
|
self.import_file, self.google_sheets_url
|
||||||
)
|
)
|
||||||
@@ -102,7 +100,7 @@ def download_errored_template(data_import_name):
|
|||||||
def parse_data_from_template(raw_data):
|
def parse_data_from_template(raw_data):
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
for i, row in enumerate(raw_data):
|
for _i, row in enumerate(raw_data):
|
||||||
if all(v in INVALID_VALUES for v in row):
|
if all(v in INVALID_VALUES for v in row):
|
||||||
# empty row
|
# empty row
|
||||||
continue
|
continue
|
||||||
@@ -112,9 +110,7 @@ def parse_data_from_template(raw_data):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def start_import(
|
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
|
||||||
data_import, bank_account, import_file_path, google_sheets_url, bank, template_options
|
|
||||||
):
|
|
||||||
"""This method runs in background job"""
|
"""This method runs in background job"""
|
||||||
|
|
||||||
update_mapping_db(bank, template_options)
|
update_mapping_db(bank, template_options)
|
||||||
|
|||||||
@@ -1,36 +1,34 @@
|
|||||||
let imports_in_progress = [];
|
let imports_in_progress = [];
|
||||||
|
|
||||||
frappe.listview_settings['Bank Statement Import'] = {
|
frappe.listview_settings["Bank Statement Import"] = {
|
||||||
onload(listview) {
|
onload(listview) {
|
||||||
frappe.realtime.on('data_import_progress', data => {
|
frappe.realtime.on("data_import_progress", (data) => {
|
||||||
if (!imports_in_progress.includes(data.data_import)) {
|
if (!imports_in_progress.includes(data.data_import)) {
|
||||||
imports_in_progress.push(data.data_import);
|
imports_in_progress.push(data.data_import);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
frappe.realtime.on('data_import_refresh', data => {
|
frappe.realtime.on("data_import_refresh", (data) => {
|
||||||
imports_in_progress = imports_in_progress.filter(
|
imports_in_progress = imports_in_progress.filter((d) => d !== data.data_import);
|
||||||
d => d !== data.data_import
|
|
||||||
);
|
|
||||||
listview.refresh();
|
listview.refresh();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
get_indicator: function(doc) {
|
get_indicator: function (doc) {
|
||||||
var colors = {
|
var colors = {
|
||||||
'Pending': 'orange',
|
Pending: "orange",
|
||||||
'Not Started': 'orange',
|
"Not Started": "orange",
|
||||||
'Partial Success': 'orange',
|
"Partial Success": "orange",
|
||||||
'Success': 'green',
|
Success: "green",
|
||||||
'In Progress': 'orange',
|
"In Progress": "orange",
|
||||||
'Error': 'red'
|
Error: "red",
|
||||||
};
|
};
|
||||||
let status = doc.status;
|
let status = doc.status;
|
||||||
if (imports_in_progress.includes(doc.name)) {
|
if (imports_in_progress.includes(doc.name)) {
|
||||||
status = 'In Progress';
|
status = "In Progress";
|
||||||
}
|
}
|
||||||
if (status == 'Pending') {
|
if (status == "Pending") {
|
||||||
status = 'Not Started';
|
status = "Not Started";
|
||||||
}
|
}
|
||||||
return [__(status), colors[status], 'status,=,' + doc.status];
|
return [__(status), colors[status], "status,=," + doc.status];
|
||||||
},
|
},
|
||||||
hide_name_column: true
|
hide_name_column: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
from typing import Tuple, Union
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from rapidfuzz import fuzz, process
|
from rapidfuzz import fuzz, process
|
||||||
@@ -19,7 +17,7 @@ class AutoMatchParty:
|
|||||||
def get(self, key):
|
def get(self, key):
|
||||||
return self.__dict__.get(key, None)
|
return self.__dict__.get(key, None)
|
||||||
|
|
||||||
def match(self) -> Union[Tuple, None]:
|
def match(self) -> tuple | None:
|
||||||
result = None
|
result = None
|
||||||
result = AutoMatchbyAccountIBAN(
|
result = AutoMatchbyAccountIBAN(
|
||||||
bank_party_account_number=self.bank_party_account_number,
|
bank_party_account_number=self.bank_party_account_number,
|
||||||
@@ -50,7 +48,7 @@ class AutoMatchbyAccountIBAN:
|
|||||||
result = self.match_account_in_party()
|
result = self.match_account_in_party()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def match_account_in_party(self) -> Union[Tuple, None]:
|
def match_account_in_party(self) -> tuple | None:
|
||||||
"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
|
"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
|
||||||
result = None
|
result = None
|
||||||
parties = get_parties_in_order(self.deposit)
|
parties = get_parties_in_order(self.deposit)
|
||||||
@@ -97,7 +95,7 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
def get(self, key):
|
def get(self, key):
|
||||||
return self.__dict__.get(key, None)
|
return self.__dict__.get(key, None)
|
||||||
|
|
||||||
def match(self) -> Union[Tuple, None]:
|
def match(self) -> tuple | None:
|
||||||
# fuzzy search by customer/supplier & employee
|
# fuzzy search by customer/supplier & employee
|
||||||
if not (self.bank_party_name or self.description):
|
if not (self.bank_party_name or self.description):
|
||||||
return None
|
return None
|
||||||
@@ -105,7 +103,7 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
result = self.match_party_name_desc_in_party()
|
result = self.match_party_name_desc_in_party()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def match_party_name_desc_in_party(self) -> Union[Tuple, None]:
|
def match_party_name_desc_in_party(self) -> tuple | None:
|
||||||
"""Fuzzy search party name and/or description against parties in the system"""
|
"""Fuzzy search party name and/or description against parties in the system"""
|
||||||
result = None
|
result = None
|
||||||
parties = get_parties_in_order(self.deposit)
|
parties = get_parties_in_order(self.deposit)
|
||||||
@@ -130,7 +128,7 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
|
def fuzzy_search_and_return_result(self, party, names, field) -> tuple | None:
|
||||||
skip = False
|
skip = False
|
||||||
result = process.extract(
|
result = process.extract(
|
||||||
query=self.get(field),
|
query=self.get(field),
|
||||||
@@ -147,7 +145,7 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
party_name,
|
party_name,
|
||||||
), skip
|
), skip
|
||||||
|
|
||||||
def process_fuzzy_result(self, result: Union[list, None]):
|
def process_fuzzy_result(self, result: list | None):
|
||||||
"""
|
"""
|
||||||
If there are multiple valid close matches return None as result may be faulty.
|
If there are multiple valid close matches return None as result may be faulty.
|
||||||
Return the result only if one accurate match stands out.
|
Return the result only if one accurate match stands out.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("Bank Transaction", {
|
frappe.ui.form.on("Bank Transaction", {
|
||||||
onload(frm) {
|
onload(frm) {
|
||||||
frm.set_query("payment_document", "payment_entries", function() {
|
frm.set_query("payment_document", "payment_entries", function () {
|
||||||
const payment_doctypes = frm.events.get_payment_doctypes(frm);
|
const payment_doctypes = frm.events.get_payment_doctypes(frm);
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
@@ -23,7 +23,7 @@ frappe.ui.form.on("Bank Transaction", {
|
|||||||
set_bank_statement_filter(frm);
|
set_bank_statement_filter(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("party_type", function () {
|
frm.set_query("party_type", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
@@ -33,16 +33,10 @@ frappe.ui.form.on("Bank Transaction", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
get_payment_doctypes: function() {
|
get_payment_doctypes: function () {
|
||||||
// get payment doctypes from all the apps
|
// get payment doctypes from all the apps
|
||||||
return [
|
return ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Bank Transaction"];
|
||||||
"Payment Entry",
|
},
|
||||||
"Journal Entry",
|
|
||||||
"Sales Invoice",
|
|
||||||
"Purchase Invoice",
|
|
||||||
"Bank Transaction",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Bank Transaction Payments", {
|
frappe.ui.form.on("Bank Transaction Payments", {
|
||||||
@@ -54,10 +48,11 @@ frappe.ui.form.on("Bank Transaction Payments", {
|
|||||||
const update_clearance_date = (frm, cdt, cdn) => {
|
const update_clearance_date = (frm, cdt, cdn) => {
|
||||||
if (frm.doc.docstatus === 1) {
|
if (frm.doc.docstatus === 1) {
|
||||||
frappe
|
frappe
|
||||||
.xcall(
|
.xcall("erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment", {
|
||||||
"erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment",
|
doctype: cdt,
|
||||||
{ doctype: cdt, docname: cdn, bt_name: frm.doc.name }
|
docname: cdn,
|
||||||
)
|
bt_name: frm.doc.name,
|
||||||
|
})
|
||||||
.then((e) => {
|
.then((e) => {
|
||||||
if (e == "success") {
|
if (e == "success") {
|
||||||
frappe.show_alert({
|
frappe.show_alert({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.model.docstatus import DocStatus
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
|
|
||||||
from erpnext.controllers.status_updater import StatusUpdater
|
from erpnext.controllers.status_updater import StatusUpdater
|
||||||
@@ -40,9 +41,10 @@ class BankTransaction(StatusUpdater):
|
|||||||
else:
|
else:
|
||||||
allocated_amount = 0.0
|
allocated_amount = 0.0
|
||||||
|
|
||||||
amount = abs(flt(self.withdrawal) - flt(self.deposit))
|
unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - allocated_amount
|
||||||
self.db_set("allocated_amount", flt(allocated_amount))
|
|
||||||
self.db_set("unallocated_amount", amount - flt(allocated_amount))
|
self.db_set("allocated_amount", flt(allocated_amount, self.precision("allocated_amount")))
|
||||||
|
self.db_set("unallocated_amount", flt(unallocated_amount, self.precision("unallocated_amount")))
|
||||||
self.reload()
|
self.reload()
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ class BankTransaction(StatusUpdater):
|
|||||||
"payment_entry": voucher["payment_name"],
|
"payment_entry": voucher["payment_name"],
|
||||||
"allocated_amount": 0.0, # Temporary
|
"allocated_amount": 0.0, # Temporary
|
||||||
}
|
}
|
||||||
child = self.append("payment_entries", pe)
|
self.append("payment_entries", pe)
|
||||||
added = True
|
added = True
|
||||||
|
|
||||||
# runs on_update_after_submit
|
# runs on_update_after_submit
|
||||||
@@ -184,9 +186,7 @@ def get_clearance_details(transaction, payment_entry):
|
|||||||
"""
|
"""
|
||||||
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||||
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
|
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
|
||||||
bt_allocations = get_total_allocated_amount(
|
bt_allocations = get_total_allocated_amount(payment_entry.payment_document, payment_entry.payment_entry)
|
||||||
payment_entry.payment_document, payment_entry.payment_entry
|
|
||||||
)
|
|
||||||
|
|
||||||
unallocated_amount = min(
|
unallocated_amount = min(
|
||||||
transaction.unallocated_amount,
|
transaction.unallocated_amount,
|
||||||
@@ -284,7 +284,6 @@ def get_total_allocated_amount(doctype, docname):
|
|||||||
|
|
||||||
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||||
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
||||||
|
|
||||||
paid_amount_field = "paid_amount"
|
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)
|
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
|
||||||
@@ -323,9 +322,7 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif payment_entry.payment_document == "Loan Repayment":
|
elif payment_entry.payment_document == "Loan Repayment":
|
||||||
return frappe.db.get_value(
|
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "amount_paid")
|
||||||
payment_entry.payment_document, payment_entry.payment_entry, "amount_paid"
|
|
||||||
)
|
|
||||||
|
|
||||||
elif payment_entry.payment_document == "Bank Transaction":
|
elif payment_entry.payment_document == "Bank Transaction":
|
||||||
dep, wth = frappe.db.get_value(
|
dep, wth = frappe.db.get_value(
|
||||||
@@ -335,9 +332,7 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
"Please reconcile {0}: {1} manually".format(
|
f"Please reconcile {payment_entry.payment_document}: {payment_entry.payment_entry} manually"
|
||||||
payment_entry.payment_document, payment_entry.payment_entry
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -393,3 +388,21 @@ def unclear_reference_payment(doctype, docname, bt_name):
|
|||||||
bt = frappe.get_doc("Bank Transaction", bt_name)
|
bt = frappe.get_doc("Bank Transaction", bt_name)
|
||||||
set_voucher_clearance(doctype, docname, None, bt)
|
set_voucher_clearance(doctype, docname, None, bt)
|
||||||
return docname
|
return docname
|
||||||
|
|
||||||
|
|
||||||
|
def remove_from_bank_transaction(doctype, docname):
|
||||||
|
"""Remove a (cancelled) voucher from all Bank Transactions."""
|
||||||
|
for bt_name in get_reconciled_bank_transactions(doctype, docname):
|
||||||
|
bt = frappe.get_doc("Bank Transaction", bt_name)
|
||||||
|
if bt.docstatus == DocStatus.cancelled():
|
||||||
|
continue
|
||||||
|
|
||||||
|
modified = False
|
||||||
|
|
||||||
|
for pe in bt.payment_entries:
|
||||||
|
if pe.payment_document == doctype and pe.payment_entry == docname:
|
||||||
|
bt.remove(pe)
|
||||||
|
modified = True
|
||||||
|
|
||||||
|
if modified:
|
||||||
|
bt.save()
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.listview_settings['Bank Transaction'] = {
|
frappe.listview_settings["Bank Transaction"] = {
|
||||||
add_fields: ["unallocated_amount"],
|
add_fields: ["unallocated_amount"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function (doc) {
|
||||||
if(doc.docstatus == 2) {
|
if (doc.docstatus == 2) {
|
||||||
return [__("Cancelled"), "red", "docstatus,=,2"];
|
return [__("Cancelled"), "red", "docstatus,=,2"];
|
||||||
} else if(flt(doc.unallocated_amount)<=0) {
|
} else if (flt(doc.unallocated_amount) <= 0) {
|
||||||
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
|
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
|
||||||
} else if(flt(doc.unallocated_amount)>0) {
|
} else if (flt(doc.unallocated_amount) > 0) {
|
||||||
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ def upload_bank_statement():
|
|||||||
fcontent = frappe.local.uploaded_file
|
fcontent = frappe.local.uploaded_file
|
||||||
fname = frappe.local.uploaded_filename
|
fname = frappe.local.uploaded_filename
|
||||||
|
|
||||||
if frappe.safe_encode(fname).lower().endswith("csv".encode("utf-8")):
|
if frappe.safe_encode(fname).lower().endswith(b"csv"):
|
||||||
from frappe.utils.csvutils import read_csv_content
|
from frappe.utils.csvutils import read_csv_content
|
||||||
|
|
||||||
rows = read_csv_content(fcontent, False)
|
rows = read_csv_content(fcontent, False)
|
||||||
|
|
||||||
elif frappe.safe_encode(fname).lower().endswith("xlsx".encode("utf-8")):
|
elif frappe.safe_encode(fname).lower().endswith(b"xlsx"):
|
||||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||||
|
|
||||||
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import utils
|
from frappe import utils
|
||||||
|
from frappe.model.docstatus import DocStatus
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||||
@@ -32,8 +32,16 @@ class TestBankTransaction(FrappeTestCase):
|
|||||||
frappe.db.delete(dt)
|
frappe.db.delete(dt)
|
||||||
|
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
add_transactions()
|
|
||||||
add_vouchers()
|
# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
|
||||||
|
uniq_identifier = frappe.generate_hash(length=10)
|
||||||
|
gl_account = create_gl_account("_Test Bank " + uniq_identifier)
|
||||||
|
bank_account = create_bank_account(
|
||||||
|
gl_account=gl_account, bank_account_name="Checking Account " + uniq_identifier
|
||||||
|
)
|
||||||
|
|
||||||
|
add_transactions(bank_account=bank_account)
|
||||||
|
add_vouchers(gl_account=gl_account)
|
||||||
|
|
||||||
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
||||||
def test_linked_payments(self):
|
def test_linked_payments(self):
|
||||||
@@ -81,6 +89,29 @@ class TestBankTransaction(FrappeTestCase):
|
|||||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||||
self.assertFalse(clearance_date)
|
self.assertFalse(clearance_date)
|
||||||
|
|
||||||
|
def test_cancel_voucher(self):
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
reconcile_vouchers(bank_transaction.name, vouchers)
|
||||||
|
payment.reload()
|
||||||
|
payment.cancel()
|
||||||
|
bank_transaction.reload()
|
||||||
|
self.assertEqual(bank_transaction.docstatus, DocStatus.submitted())
|
||||||
|
self.assertEqual(bank_transaction.unallocated_amount, 1700)
|
||||||
|
self.assertEqual(bank_transaction.payment_entries, [])
|
||||||
|
|
||||||
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
|
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
|
||||||
def test_debit_credit_output(self):
|
def test_debit_credit_output(self):
|
||||||
bank_transaction = frappe.get_doc(
|
bank_transaction = frappe.get_doc(
|
||||||
@@ -190,7 +221,9 @@ class TestBankTransaction(FrappeTestCase):
|
|||||||
self.assertEqual(linked_payments[0][2], repayment_entry.name)
|
self.assertEqual(linked_payments[0][2], repayment_entry.name)
|
||||||
|
|
||||||
|
|
||||||
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
def create_bank_account(
|
||||||
|
bank_name="Citi Bank", gl_account="_Test Bank - _TC", bank_account_name="Checking Account"
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
frappe.get_doc(
|
frappe.get_doc(
|
||||||
{
|
{
|
||||||
@@ -202,21 +235,35 @@ def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
frappe.get_doc(
|
bank_account = frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Bank Account",
|
"doctype": "Bank Account",
|
||||||
"account_name": "Checking Account",
|
"account_name": bank_account_name,
|
||||||
"bank": bank_name,
|
"bank": bank_name,
|
||||||
"account": account_name,
|
"account": gl_account,
|
||||||
}
|
}
|
||||||
).insert(ignore_if_duplicate=True)
|
).insert(ignore_if_duplicate=True)
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
return bank_account.name
|
||||||
|
|
||||||
def add_transactions():
|
|
||||||
create_bank_account()
|
|
||||||
|
|
||||||
|
def create_gl_account(gl_account_name="_Test Bank - _TC"):
|
||||||
|
gl_account = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Account",
|
||||||
|
"company": "_Test Company",
|
||||||
|
"parent_account": "Current Assets - _TC",
|
||||||
|
"account_type": "Bank",
|
||||||
|
"is_group": 0,
|
||||||
|
"account_name": gl_account_name,
|
||||||
|
}
|
||||||
|
).insert()
|
||||||
|
return gl_account.name
|
||||||
|
|
||||||
|
|
||||||
|
def add_transactions(bank_account="_Test Bank - _TC"):
|
||||||
doc = frappe.get_doc(
|
doc = frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
@@ -224,7 +271,7 @@ def add_transactions():
|
|||||||
"date": "2018-10-23",
|
"date": "2018-10-23",
|
||||||
"deposit": 1200,
|
"deposit": 1200,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank",
|
"bank_account": bank_account,
|
||||||
}
|
}
|
||||||
).insert()
|
).insert()
|
||||||
doc.submit()
|
doc.submit()
|
||||||
@@ -236,7 +283,7 @@ def add_transactions():
|
|||||||
"date": "2018-10-23",
|
"date": "2018-10-23",
|
||||||
"deposit": 1700,
|
"deposit": 1700,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank",
|
"bank_account": bank_account,
|
||||||
}
|
}
|
||||||
).insert()
|
).insert()
|
||||||
doc.submit()
|
doc.submit()
|
||||||
@@ -248,7 +295,7 @@ def add_transactions():
|
|||||||
"date": "2018-10-26",
|
"date": "2018-10-26",
|
||||||
"withdrawal": 690,
|
"withdrawal": 690,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank",
|
"bank_account": bank_account,
|
||||||
}
|
}
|
||||||
).insert()
|
).insert()
|
||||||
doc.submit()
|
doc.submit()
|
||||||
@@ -260,7 +307,7 @@ def add_transactions():
|
|||||||
"date": "2018-10-27",
|
"date": "2018-10-27",
|
||||||
"deposit": 3900,
|
"deposit": 3900,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank",
|
"bank_account": bank_account,
|
||||||
}
|
}
|
||||||
).insert()
|
).insert()
|
||||||
doc.submit()
|
doc.submit()
|
||||||
@@ -272,13 +319,13 @@ def add_transactions():
|
|||||||
"date": "2018-10-27",
|
"date": "2018-10-27",
|
||||||
"withdrawal": 109080,
|
"withdrawal": 109080,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank",
|
"bank_account": bank_account,
|
||||||
}
|
}
|
||||||
).insert()
|
).insert()
|
||||||
doc.submit()
|
doc.submit()
|
||||||
|
|
||||||
|
|
||||||
def add_vouchers():
|
def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||||
try:
|
try:
|
||||||
frappe.get_doc(
|
frappe.get_doc(
|
||||||
{
|
{
|
||||||
@@ -294,7 +341,7 @@ def add_vouchers():
|
|||||||
|
|
||||||
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
|
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
|
||||||
|
|
||||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||||
pe.reference_no = "Conrad Oct 18"
|
pe.reference_no = "Conrad Oct 18"
|
||||||
pe.reference_date = "2018-10-24"
|
pe.reference_date = "2018-10-24"
|
||||||
pe.insert()
|
pe.insert()
|
||||||
@@ -313,14 +360,14 @@ def add_vouchers():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200)
|
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200)
|
||||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||||
pe.reference_no = "Herr G Oct 18"
|
pe.reference_no = "Herr G Oct 18"
|
||||||
pe.reference_date = "2018-10-24"
|
pe.reference_date = "2018-10-24"
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700)
|
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700)
|
||||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||||
pe.reference_no = "Herr G Nov 18"
|
pe.reference_no = "Herr G Nov 18"
|
||||||
pe.reference_date = "2018-11-01"
|
pe.reference_date = "2018-11-01"
|
||||||
pe.insert()
|
pe.insert()
|
||||||
@@ -351,10 +398,10 @@ def add_vouchers():
|
|||||||
pass
|
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.cash_bank_account = gl_account
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||||
pe.reference_no = "Poore Simon's Oct 18"
|
pe.reference_no = "Poore Simon's Oct 18"
|
||||||
pe.reference_date = "2018-10-28"
|
pe.reference_date = "2018-10-28"
|
||||||
pe.paid_amount = 690
|
pe.paid_amount = 690
|
||||||
@@ -363,7 +410,7 @@ def add_vouchers():
|
|||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900)
|
si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900)
|
||||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account=gl_account)
|
||||||
pe.reference_no = "Poore Simon's Oct 18"
|
pe.reference_no = "Poore Simon's Oct 18"
|
||||||
pe.reference_date = "2018-10-28"
|
pe.reference_date = "2018-10-28"
|
||||||
pe.insert()
|
pe.insert()
|
||||||
@@ -383,19 +430,13 @@ def add_vouchers():
|
|||||||
|
|
||||||
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(
|
if not frappe.db.get_value("Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}):
|
||||||
"Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
|
mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account})
|
||||||
):
|
|
||||||
mode_of_payment.append(
|
|
||||||
"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
|
|
||||||
)
|
|
||||||
mode_of_payment.save()
|
mode_of_payment.save()
|
||||||
|
|
||||||
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
|
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
|
||||||
si.is_pos = 1
|
si.is_pos = 1
|
||||||
si.append(
|
si.append("payments", {"mode_of_payment": "Cash", "account": gl_account, "amount": 109080})
|
||||||
"payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080}
|
|
||||||
)
|
|
||||||
si.insert()
|
si.insert()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Bisect Accounting Statements", {
|
||||||
|
onload(frm) {
|
||||||
|
frm.trigger("render_heatmap");
|
||||||
|
},
|
||||||
|
refresh(frm) {
|
||||||
|
frm.add_custom_button(__("Bisect Left"), () => {
|
||||||
|
frm.trigger("bisect_left");
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.add_custom_button(__("Bisect Right"), () => {
|
||||||
|
frm.trigger("bisect_right");
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.add_custom_button(__("Up"), () => {
|
||||||
|
frm.trigger("move_up");
|
||||||
|
});
|
||||||
|
frm.add_custom_button(__("Build Tree"), () => {
|
||||||
|
frm.trigger("build_tree");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
render_heatmap(frm) {
|
||||||
|
let bisect_heatmap = frm.get_field("bisect_heatmap").$wrapper;
|
||||||
|
bisect_heatmap.addClass("bisect_heatmap_location");
|
||||||
|
|
||||||
|
// milliseconds in a day
|
||||||
|
let msiad = 24 * 60 * 60 * 1000;
|
||||||
|
let datapoints = {};
|
||||||
|
let fr_dt = new Date(frm.doc.from_date).getTime();
|
||||||
|
let to_dt = new Date(frm.doc.to_date).getTime();
|
||||||
|
let bisect_start = new Date(frm.doc.current_from_date).getTime();
|
||||||
|
let bisect_end = new Date(frm.doc.current_to_date).getTime();
|
||||||
|
|
||||||
|
for (let x = fr_dt; x <= to_dt; x += msiad) {
|
||||||
|
let epoch_in_seconds = x / 1000;
|
||||||
|
if (bisect_start <= x && x <= bisect_end) {
|
||||||
|
datapoints[epoch_in_seconds] = 1.0;
|
||||||
|
} else {
|
||||||
|
datapoints[epoch_in_seconds] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new frappe.Chart(".bisect_heatmap_location", {
|
||||||
|
type: "heatmap",
|
||||||
|
data: {
|
||||||
|
dataPoints: datapoints,
|
||||||
|
start: new Date(frm.doc.from_date),
|
||||||
|
end: new Date(frm.doc.to_date),
|
||||||
|
},
|
||||||
|
countLabel: "Bisecting",
|
||||||
|
discreteDomains: 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
bisect_left(frm) {
|
||||||
|
frm.call({
|
||||||
|
doc: frm.doc,
|
||||||
|
method: "bisect_left",
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __("Bisecting Left ..."),
|
||||||
|
callback: (r) => {
|
||||||
|
frm.trigger("render_heatmap");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
bisect_right(frm) {
|
||||||
|
frm.call({
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __("Bisecting Right ..."),
|
||||||
|
method: "bisect_right",
|
||||||
|
callback: (r) => {
|
||||||
|
frm.trigger("render_heatmap");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
move_up(frm) {
|
||||||
|
frm.call({
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __("Moving up in tree ..."),
|
||||||
|
method: "move_up",
|
||||||
|
callback: (r) => {
|
||||||
|
frm.trigger("render_heatmap");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
build_tree(frm) {
|
||||||
|
frm.call({
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __("Rebuilding BTree for period ..."),
|
||||||
|
method: "build_tree",
|
||||||
|
callback: (r) => {
|
||||||
|
frm.trigger("render_heatmap");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2023-09-15 21:28:28.054773",
|
||||||
|
"default_view": "List",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"section_break_cvfg",
|
||||||
|
"company",
|
||||||
|
"column_break_hcam",
|
||||||
|
"from_date",
|
||||||
|
"column_break_qxbi",
|
||||||
|
"to_date",
|
||||||
|
"column_break_iwny",
|
||||||
|
"algorithm",
|
||||||
|
"section_break_8ph9",
|
||||||
|
"current_node",
|
||||||
|
"section_break_ngid",
|
||||||
|
"bisect_heatmap",
|
||||||
|
"section_break_hmsy",
|
||||||
|
"bisecting_from",
|
||||||
|
"current_from_date",
|
||||||
|
"column_break_uqyd",
|
||||||
|
"bisecting_to",
|
||||||
|
"current_to_date",
|
||||||
|
"section_break_hbyo",
|
||||||
|
"heading_cppb",
|
||||||
|
"p_l_summary",
|
||||||
|
"column_break_aivo",
|
||||||
|
"balance_sheet_summary",
|
||||||
|
"b_s_summary",
|
||||||
|
"column_break_gvwx",
|
||||||
|
"difference_heading",
|
||||||
|
"difference"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_qxbi",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "from_date",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"label": "From Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "to_date",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"label": "To Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "BFS",
|
||||||
|
"fieldname": "algorithm",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Algorithm",
|
||||||
|
"options": "BFS\nDFS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_iwny",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "current_node",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Current Node",
|
||||||
|
"options": "Bisect Nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_hmsy",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "current_from_date",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "current_to_date",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_uqyd",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_hbyo",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "p_l_summary",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "b_s_summary",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_aivo",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_gvwx",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_hcam",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_ngid",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_8ph9",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "bisect_heatmap",
|
||||||
|
"fieldtype": "HTML",
|
||||||
|
"label": "Heatmap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "heading_cppb",
|
||||||
|
"fieldtype": "Heading",
|
||||||
|
"label": "Profit and Loss Summary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "balance_sheet_summary",
|
||||||
|
"fieldtype": "Heading",
|
||||||
|
"label": "Balance Sheet Summary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference_heading",
|
||||||
|
"fieldtype": "Heading",
|
||||||
|
"label": "Difference"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "bisecting_from",
|
||||||
|
"fieldtype": "Heading",
|
||||||
|
"label": "Bisecting From"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "bisecting_to",
|
||||||
|
"fieldtype": "Heading",
|
||||||
|
"label": "Bisecting To"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_cvfg",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_toolbar": 1,
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"issingle": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-12-01 16:49:54.073890",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Bisect Accounting Statements",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"role": "Administrator",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"read_only": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from collections import deque
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import getdate
|
||||||
|
from frappe.utils.data import guess_date_format
|
||||||
|
|
||||||
|
|
||||||
|
class BisectAccountingStatements(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_dates()
|
||||||
|
|
||||||
|
def validate_dates(self):
|
||||||
|
if getdate(self.from_date) > getdate(self.to_date):
|
||||||
|
frappe.throw(
|
||||||
|
_("From Date: {0} cannot be greater than To date: {1}").format(
|
||||||
|
frappe.bold(self.from_date), frappe.bold(self.to_date)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def bfs(self, from_date: datetime, to_date: datetime):
|
||||||
|
# Make Root node
|
||||||
|
node = frappe.new_doc("Bisect Nodes")
|
||||||
|
node.root = None
|
||||||
|
node.period_from_date = from_date
|
||||||
|
node.period_to_date = to_date
|
||||||
|
node.insert()
|
||||||
|
|
||||||
|
period_queue = deque([node])
|
||||||
|
while period_queue:
|
||||||
|
cur_node = period_queue.popleft()
|
||||||
|
delta = cur_node.period_to_date - cur_node.period_from_date
|
||||||
|
if delta.days == 0:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
cur_floor = floor(delta.days / 2)
|
||||||
|
next_to_date = cur_node.period_from_date + relativedelta(days=+cur_floor)
|
||||||
|
left_node = frappe.new_doc("Bisect Nodes")
|
||||||
|
left_node.period_from_date = cur_node.period_from_date
|
||||||
|
left_node.period_to_date = next_to_date
|
||||||
|
left_node.root = cur_node.name
|
||||||
|
left_node.generated = False
|
||||||
|
left_node.insert()
|
||||||
|
cur_node.left_child = left_node.name
|
||||||
|
period_queue.append(left_node)
|
||||||
|
|
||||||
|
next_from_date = cur_node.period_from_date + relativedelta(days=+(cur_floor + 1))
|
||||||
|
right_node = frappe.new_doc("Bisect Nodes")
|
||||||
|
right_node.period_from_date = next_from_date
|
||||||
|
right_node.period_to_date = cur_node.period_to_date
|
||||||
|
right_node.root = cur_node.name
|
||||||
|
right_node.generated = False
|
||||||
|
right_node.insert()
|
||||||
|
cur_node.right_child = right_node.name
|
||||||
|
period_queue.append(right_node)
|
||||||
|
|
||||||
|
cur_node.save()
|
||||||
|
|
||||||
|
def dfs(self, from_date: datetime, to_date: datetime):
|
||||||
|
# Make Root node
|
||||||
|
node = frappe.new_doc("Bisect Nodes")
|
||||||
|
node.root = None
|
||||||
|
node.period_from_date = from_date
|
||||||
|
node.period_to_date = to_date
|
||||||
|
node.insert()
|
||||||
|
|
||||||
|
period_stack = [node]
|
||||||
|
while period_stack:
|
||||||
|
cur_node = period_stack.pop()
|
||||||
|
delta = cur_node.period_to_date - cur_node.period_from_date
|
||||||
|
if delta.days == 0:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
cur_floor = floor(delta.days / 2)
|
||||||
|
next_to_date = cur_node.period_from_date + relativedelta(days=+cur_floor)
|
||||||
|
left_node = frappe.new_doc("Bisect Nodes")
|
||||||
|
left_node.period_from_date = cur_node.period_from_date
|
||||||
|
left_node.period_to_date = next_to_date
|
||||||
|
left_node.root = cur_node.name
|
||||||
|
left_node.generated = False
|
||||||
|
left_node.insert()
|
||||||
|
cur_node.left_child = left_node.name
|
||||||
|
period_stack.append(left_node)
|
||||||
|
|
||||||
|
next_from_date = cur_node.period_from_date + relativedelta(days=+(cur_floor + 1))
|
||||||
|
right_node = frappe.new_doc("Bisect Nodes")
|
||||||
|
right_node.period_from_date = next_from_date
|
||||||
|
right_node.period_to_date = cur_node.period_to_date
|
||||||
|
right_node.root = cur_node.name
|
||||||
|
right_node.generated = False
|
||||||
|
right_node.insert()
|
||||||
|
cur_node.right_child = right_node.name
|
||||||
|
period_stack.append(right_node)
|
||||||
|
|
||||||
|
cur_node.save()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def build_tree(self):
|
||||||
|
frappe.db.delete("Bisect Nodes")
|
||||||
|
|
||||||
|
# Convert str to datetime format
|
||||||
|
dt_format = guess_date_format(self.from_date)
|
||||||
|
from_date = datetime.datetime.strptime(self.from_date, dt_format)
|
||||||
|
to_date = datetime.datetime.strptime(self.to_date, dt_format)
|
||||||
|
|
||||||
|
if self.algorithm == "BFS":
|
||||||
|
self.bfs(from_date, to_date)
|
||||||
|
|
||||||
|
if self.algorithm == "DFS":
|
||||||
|
self.dfs(from_date, to_date)
|
||||||
|
|
||||||
|
# set root as current node
|
||||||
|
root = frappe.db.get_all("Bisect Nodes", filters={"root": ["is", "not set"]})[0]
|
||||||
|
self.get_report_summary()
|
||||||
|
self.current_node = root.name
|
||||||
|
self.current_from_date = self.from_date
|
||||||
|
self.current_to_date = self.to_date
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def get_report_summary(self):
|
||||||
|
filters = {
|
||||||
|
"company": self.company,
|
||||||
|
"filter_based_on": "Date Range",
|
||||||
|
"period_start_date": self.current_from_date,
|
||||||
|
"period_end_date": self.current_to_date,
|
||||||
|
"periodicity": "Yearly",
|
||||||
|
}
|
||||||
|
pl_summary = frappe.get_doc("Report", "Profit and Loss Statement")
|
||||||
|
self.p_l_summary = pl_summary.execute_script_report(filters=filters)[5]
|
||||||
|
bs_summary = frappe.get_doc("Report", "Balance Sheet")
|
||||||
|
self.b_s_summary = bs_summary.execute_script_report(filters=filters)[5]
|
||||||
|
self.difference = abs(self.p_l_summary - self.b_s_summary)
|
||||||
|
|
||||||
|
def update_node(self):
|
||||||
|
current_node = frappe.get_doc("Bisect Nodes", self.current_node)
|
||||||
|
current_node.balance_sheet_summary = self.b_s_summary
|
||||||
|
current_node.profit_loss_summary = self.p_l_summary
|
||||||
|
current_node.difference = self.difference
|
||||||
|
current_node.generated = True
|
||||||
|
current_node.save()
|
||||||
|
|
||||||
|
def current_node_has_summary_info(self):
|
||||||
|
"Assertion method"
|
||||||
|
return frappe.db.get_value("Bisect Nodes", self.current_node, "generated")
|
||||||
|
|
||||||
|
def fetch_summary_info_from_current_node(self):
|
||||||
|
current_node = frappe.get_doc("Bisect Nodes", self.current_node)
|
||||||
|
self.p_l_summary = current_node.balance_sheet_summary
|
||||||
|
self.b_s_summary = current_node.profit_loss_summary
|
||||||
|
self.difference = abs(self.p_l_summary - self.b_s_summary)
|
||||||
|
|
||||||
|
def fetch_or_calculate(self):
|
||||||
|
if self.current_node_has_summary_info():
|
||||||
|
self.fetch_summary_info_from_current_node()
|
||||||
|
else:
|
||||||
|
self.get_report_summary()
|
||||||
|
self.update_node()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def bisect_left(self):
|
||||||
|
if self.current_node is not None:
|
||||||
|
cur_node = frappe.get_doc("Bisect Nodes", self.current_node)
|
||||||
|
if cur_node.left_child is not None:
|
||||||
|
lft_node = frappe.get_doc("Bisect Nodes", cur_node.left_child)
|
||||||
|
self.current_node = cur_node.left_child
|
||||||
|
self.current_from_date = lft_node.period_from_date
|
||||||
|
self.current_to_date = lft_node.period_to_date
|
||||||
|
self.fetch_or_calculate()
|
||||||
|
self.save()
|
||||||
|
else:
|
||||||
|
frappe.msgprint(_("No more children on Left"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def bisect_right(self):
|
||||||
|
if self.current_node is not None:
|
||||||
|
cur_node = frappe.get_doc("Bisect Nodes", self.current_node)
|
||||||
|
if cur_node.right_child is not None:
|
||||||
|
rgt_node = frappe.get_doc("Bisect Nodes", cur_node.right_child)
|
||||||
|
self.current_node = cur_node.right_child
|
||||||
|
self.current_from_date = rgt_node.period_from_date
|
||||||
|
self.current_to_date = rgt_node.period_to_date
|
||||||
|
self.fetch_or_calculate()
|
||||||
|
self.save()
|
||||||
|
else:
|
||||||
|
frappe.msgprint(_("No more children on Right"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def move_up(self):
|
||||||
|
if self.current_node is not None:
|
||||||
|
cur_node = frappe.get_doc("Bisect Nodes", self.current_node)
|
||||||
|
if cur_node.root is not None:
|
||||||
|
root = frappe.get_doc("Bisect Nodes", cur_node.root)
|
||||||
|
self.current_node = cur_node.root
|
||||||
|
self.current_from_date = root.period_from_date
|
||||||
|
self.current_to_date = root.period_to_date
|
||||||
|
self.fetch_or_calculate()
|
||||||
|
self.save()
|
||||||
|
else:
|
||||||
|
frappe.msgprint(_("Reached Root"))
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestBisectAccountingStatements(FrappeTestCase):
|
||||||
|
pass
|
||||||
0
erpnext/accounts/doctype/bisect_nodes/__init__.py
Normal file
0
erpnext/accounts/doctype/bisect_nodes/__init__.py
Normal file
8
erpnext/accounts/doctype/bisect_nodes/bisect_nodes.js
Normal file
8
erpnext/accounts/doctype/bisect_nodes/bisect_nodes.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
// frappe.ui.form.on("Bisect Nodes", {
|
||||||
|
// refresh(frm) {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
97
erpnext/accounts/doctype/bisect_nodes/bisect_nodes.json
Normal file
97
erpnext/accounts/doctype/bisect_nodes/bisect_nodes.json
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "autoincrement",
|
||||||
|
"creation": "2023-09-27 14:56:38.112462",
|
||||||
|
"default_view": "List",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"root",
|
||||||
|
"left_child",
|
||||||
|
"right_child",
|
||||||
|
"period_from_date",
|
||||||
|
"period_to_date",
|
||||||
|
"difference",
|
||||||
|
"balance_sheet_summary",
|
||||||
|
"profit_loss_summary",
|
||||||
|
"generated"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "root",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Root",
|
||||||
|
"options": "Bisect Nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "left_child",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Left Child",
|
||||||
|
"options": "Bisect Nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "right_child",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Right Child",
|
||||||
|
"options": "Bisect Nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "period_from_date",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"label": "Period_from_date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "period_to_date",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"label": "Period To Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Difference"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "balance_sheet_summary",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Balance Sheet Summary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "profit_loss_summary",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Profit and Loss Summary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "generated",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Generated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-12-01 17:46:12.437996",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Bisect Nodes",
|
||||||
|
"naming_rule": "Autoincrement",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Administrator",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"read_only": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
29
erpnext/accounts/doctype/bisect_nodes/bisect_nodes.py
Normal file
29
erpnext/accounts/doctype/bisect_nodes/bisect_nodes.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class BisectNodes(Document):
|
||||||
|
# begin: auto-generated types
|
||||||
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from frappe.types import DF
|
||||||
|
|
||||||
|
balance_sheet_summary: DF.Float
|
||||||
|
difference: DF.Float
|
||||||
|
generated: DF.Check
|
||||||
|
left_child: DF.Link | None
|
||||||
|
name: DF.Int | None
|
||||||
|
period_from_date: DF.Datetime | None
|
||||||
|
period_to_date: DF.Datetime | None
|
||||||
|
profit_loss_summary: DF.Float
|
||||||
|
right_child: DF.Link | None
|
||||||
|
root: DF.Link | None
|
||||||
|
# end: auto-generated types
|
||||||
|
|
||||||
|
pass
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestBisectNodes(FrappeTestCase):
|
||||||
|
pass
|
||||||
@@ -2,48 +2,48 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
frappe.provide("erpnext.accounts.dimensions");
|
frappe.provide("erpnext.accounts.dimensions");
|
||||||
|
|
||||||
frappe.ui.form.on('Budget', {
|
frappe.ui.form.on("Budget", {
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
frm.set_query("account", "accounts", function() {
|
frm.set_query("account", "accounts", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
report_type: "Profit and Loss",
|
report_type: "Profit and Loss",
|
||||||
is_group: 0
|
is_group: 0,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("monthly_distribution", function() {
|
frm.set_query("monthly_distribution", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
fiscal_year: frm.doc.fiscal_year
|
fiscal_year: frm.doc.fiscal_year,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frm.trigger("toggle_reqd_fields")
|
frm.trigger("toggle_reqd_fields");
|
||||||
},
|
},
|
||||||
|
|
||||||
budget_against: function(frm) {
|
budget_against: function (frm) {
|
||||||
frm.trigger("set_null_value")
|
frm.trigger("set_null_value");
|
||||||
frm.trigger("toggle_reqd_fields")
|
frm.trigger("toggle_reqd_fields");
|
||||||
},
|
},
|
||||||
|
|
||||||
set_null_value: function(frm) {
|
set_null_value: function (frm) {
|
||||||
if(frm.doc.budget_against == 'Cost Center') {
|
if (frm.doc.budget_against == "Cost Center") {
|
||||||
frm.set_value('project', null)
|
frm.set_value("project", null);
|
||||||
} else {
|
} else {
|
||||||
frm.set_value('cost_center', null)
|
frm.set_value("cost_center", null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle_reqd_fields: function(frm) {
|
toggle_reqd_fields: function (frm) {
|
||||||
frm.toggle_reqd("cost_center", frm.doc.budget_against=="Cost Center");
|
frm.toggle_reqd("cost_center", frm.doc.budget_against == "Cost Center");
|
||||||
frm.toggle_reqd("project", frm.doc.budget_against=="Project");
|
frm.toggle_reqd("project", frm.doc.budget_against == "Project");
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,10 +40,11 @@ class Budget(Document):
|
|||||||
select
|
select
|
||||||
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
|
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
|
||||||
where
|
where
|
||||||
ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and
|
ba.parent = b.name and b.docstatus < 2 and b.company = {} and {}={} and
|
||||||
b.fiscal_year=%s and b.name != %s and ba.account in (%s) """
|
b.fiscal_year={} and b.name != {} and ba.account in ({}) """.format(
|
||||||
% ("%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))),
|
"%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))
|
||||||
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts),
|
),
|
||||||
|
(self.company, budget_against, self.fiscal_year, self.name, *tuple(accounts)),
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,12 +67,14 @@ class Budget(Document):
|
|||||||
if account_details.is_group:
|
if account_details.is_group:
|
||||||
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
||||||
elif account_details.company != self.company:
|
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":
|
elif account_details.report_type != "Profit and Loss":
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format(
|
_(
|
||||||
d.account
|
"Budget cannot be assigned against {0}, as it's not an Income or Expense account"
|
||||||
)
|
).format(d.account)
|
||||||
)
|
)
|
||||||
|
|
||||||
if d.account in account_list:
|
if d.account in account_list:
|
||||||
@@ -109,6 +112,8 @@ class Budget(Document):
|
|||||||
|
|
||||||
def validate_expense_against_budget(args, expense_amount=0):
|
def validate_expense_against_budget(args, expense_amount=0):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
if not frappe.get_all("Budget", limit=1):
|
||||||
|
return
|
||||||
|
|
||||||
if args.get("company") and not args.fiscal_year:
|
if args.get("company") and not args.fiscal_year:
|
||||||
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
|
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
|
||||||
@@ -116,6 +121,9 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
"Company", args.get("company"), "exception_budget_approver_role"
|
"Company", args.get("company"), "exception_budget_approver_role"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not frappe.get_cached_value("Budget", {"fiscal_year": args.fiscal_year, "company": args.company}): # nosec
|
||||||
|
return
|
||||||
|
|
||||||
if not args.account:
|
if not args.account:
|
||||||
args.account = args.get("expense_account")
|
args.account = args.get("expense_account")
|
||||||
|
|
||||||
@@ -142,32 +150,26 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
if (
|
if (
|
||||||
args.get(budget_against)
|
args.get(budget_against)
|
||||||
and args.account
|
and args.account
|
||||||
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
|
and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense")
|
||||||
):
|
):
|
||||||
|
|
||||||
doctype = dimension.get("document_type")
|
doctype = dimension.get("document_type")
|
||||||
|
|
||||||
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"])
|
lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||||
condition = """and exists(select name from `tab%s`
|
condition = f"""and exists(select name from `tab{doctype}`
|
||||||
where lft<=%s and rgt>=%s and name=b.%s)""" % (
|
where lft<={lft} and rgt>={rgt} and name=b.{budget_against})""" # nosec
|
||||||
doctype,
|
|
||||||
lft,
|
|
||||||
rgt,
|
|
||||||
budget_against,
|
|
||||||
) # nosec
|
|
||||||
args.is_tree = True
|
args.is_tree = True
|
||||||
else:
|
else:
|
||||||
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
|
condition = f"and b.{budget_against}={frappe.db.escape(args.get(budget_against))}"
|
||||||
args.is_tree = False
|
args.is_tree = False
|
||||||
|
|
||||||
args.budget_against_field = budget_against
|
args.budget_against_field = budget_against
|
||||||
args.budget_against_doctype = doctype
|
args.budget_against_doctype = doctype
|
||||||
|
|
||||||
budget_records = frappe.db.sql(
|
budget_records = frappe.db.sql(
|
||||||
"""
|
f"""
|
||||||
select
|
select
|
||||||
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
|
b.{budget_against} as budget_against, ba.budget_amount, b.monthly_distribution,
|
||||||
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
ifnull(b.applicable_on_material_request, 0) as for_material_request,
|
||||||
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
|
||||||
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
|
||||||
@@ -180,9 +182,7 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
b.name=ba.parent and b.fiscal_year=%s
|
b.name=ba.parent and b.fiscal_year=%s
|
||||||
and ba.account=%s and b.docstatus=1
|
and ba.account=%s and b.docstatus=1
|
||||||
{condition}
|
{condition}
|
||||||
""".format(
|
""",
|
||||||
condition=condition, budget_against_field=budget_against
|
|
||||||
),
|
|
||||||
(args.fiscal_year, args.account),
|
(args.fiscal_year, args.account),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
) # nosec
|
) # nosec
|
||||||
@@ -194,12 +194,18 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
def validate_budget_records(args, budget_records, expense_amount):
|
def validate_budget_records(args, budget_records, expense_amount):
|
||||||
for budget in budget_records:
|
for budget in budget_records:
|
||||||
if flt(budget.budget_amount):
|
if flt(budget.budget_amount):
|
||||||
amount = expense_amount or get_amount(args, budget)
|
|
||||||
yearly_action, monthly_action = get_actions(args, budget)
|
yearly_action, monthly_action = get_actions(args, budget)
|
||||||
|
args["for_material_request"] = budget.for_material_request
|
||||||
|
args["for_purchase_order"] = budget.for_purchase_order
|
||||||
|
|
||||||
if yearly_action in ("Stop", "Warn"):
|
if yearly_action in ("Stop", "Warn"):
|
||||||
compare_expense_with_budget(
|
compare_expense_with_budget(
|
||||||
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
|
args,
|
||||||
|
flt(budget.budget_amount),
|
||||||
|
_("Annual"),
|
||||||
|
yearly_action,
|
||||||
|
budget.budget_against,
|
||||||
|
expense_amount,
|
||||||
)
|
)
|
||||||
|
|
||||||
if monthly_action in ["Stop", "Warn"]:
|
if monthly_action in ["Stop", "Warn"]:
|
||||||
@@ -210,18 +216,32 @@ def validate_budget_records(args, budget_records, expense_amount):
|
|||||||
args["month_end_date"] = get_last_day(args.posting_date)
|
args["month_end_date"] = get_last_day(args.posting_date)
|
||||||
|
|
||||||
compare_expense_with_budget(
|
compare_expense_with_budget(
|
||||||
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
|
args,
|
||||||
|
budget_amount,
|
||||||
|
_("Accumulated Monthly"),
|
||||||
|
monthly_action,
|
||||||
|
budget.budget_against,
|
||||||
|
expense_amount,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
||||||
actual_expense = get_actual_expense(args)
|
args.actual_expense, args.requested_amount, args.ordered_amount = get_actual_expense(args), 0, 0
|
||||||
total_expense = actual_expense + amount
|
if not amount:
|
||||||
|
args.requested_amount, args.ordered_amount = get_requested_amount(args), get_ordered_amount(args)
|
||||||
|
|
||||||
|
if args.get("doctype") == "Material Request" and args.for_material_request:
|
||||||
|
amount = args.requested_amount + args.ordered_amount
|
||||||
|
|
||||||
|
elif args.get("doctype") == "Purchase Order" and args.for_purchase_order:
|
||||||
|
amount = args.ordered_amount
|
||||||
|
|
||||||
|
total_expense = args.actual_expense + amount
|
||||||
|
|
||||||
if total_expense > budget_amount:
|
if total_expense > budget_amount:
|
||||||
if actual_expense > budget_amount:
|
if args.actual_expense > budget_amount:
|
||||||
error_tense = _("is already")
|
error_tense = _("is already")
|
||||||
diff = actual_expense - budget_amount
|
diff = args.actual_expense - budget_amount
|
||||||
else:
|
else:
|
||||||
error_tense = _("will be")
|
error_tense = _("will be")
|
||||||
diff = total_expense - budget_amount
|
diff = total_expense - budget_amount
|
||||||
@@ -238,9 +258,10 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
|||||||
frappe.bold(fmt_money(diff, currency=currency)),
|
frappe.bold(fmt_money(diff, currency=currency)),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
msg += get_expense_breakup(args, currency, budget_against)
|
||||||
frappe.flags.exception_approver_role
|
|
||||||
and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)
|
if frappe.flags.exception_approver_role and frappe.flags.exception_approver_role in frappe.get_roles(
|
||||||
|
frappe.session.user
|
||||||
):
|
):
|
||||||
action = "Warn"
|
action = "Warn"
|
||||||
|
|
||||||
@@ -250,6 +271,83 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
|||||||
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
|
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_expense_breakup(args, currency, budget_against):
|
||||||
|
msg = "<hr>Total Expenses booked through - <ul>"
|
||||||
|
|
||||||
|
common_filters = frappe._dict(
|
||||||
|
{
|
||||||
|
args.budget_against_field: budget_against,
|
||||||
|
"account": args.account,
|
||||||
|
"company": args.company,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
msg += (
|
||||||
|
"<li>"
|
||||||
|
+ frappe.utils.get_link_to_report(
|
||||||
|
"General Ledger",
|
||||||
|
label="Actual Expenses",
|
||||||
|
filters=common_filters.copy().update(
|
||||||
|
{
|
||||||
|
"from_date": frappe.get_cached_value("Fiscal Year", args.fiscal_year, "year_start_date"),
|
||||||
|
"to_date": frappe.get_cached_value("Fiscal Year", args.fiscal_year, "year_end_date"),
|
||||||
|
"is_cancelled": 0,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ " - "
|
||||||
|
+ frappe.bold(fmt_money(args.actual_expense, currency=currency))
|
||||||
|
+ "</li>"
|
||||||
|
)
|
||||||
|
|
||||||
|
msg += (
|
||||||
|
"<li>"
|
||||||
|
+ frappe.utils.get_link_to_report(
|
||||||
|
"Material Request",
|
||||||
|
label="Material Requests",
|
||||||
|
report_type="Report Builder",
|
||||||
|
doctype="Material Request",
|
||||||
|
filters=common_filters.copy().update(
|
||||||
|
{
|
||||||
|
"status": [["!=", "Stopped"]],
|
||||||
|
"docstatus": 1,
|
||||||
|
"material_request_type": "Purchase",
|
||||||
|
"schedule_date": [["fiscal year", "2023-2024"]],
|
||||||
|
"item_code": args.item_code,
|
||||||
|
"per_ordered": [["<", 100]],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ " - "
|
||||||
|
+ frappe.bold(fmt_money(args.requested_amount, currency=currency))
|
||||||
|
+ "</li>"
|
||||||
|
)
|
||||||
|
|
||||||
|
msg += (
|
||||||
|
"<li>"
|
||||||
|
+ frappe.utils.get_link_to_report(
|
||||||
|
"Purchase Order",
|
||||||
|
label="Unbilled Orders",
|
||||||
|
report_type="Report Builder",
|
||||||
|
doctype="Purchase Order",
|
||||||
|
filters=common_filters.copy().update(
|
||||||
|
{
|
||||||
|
"status": [["!=", "Closed"]],
|
||||||
|
"docstatus": 1,
|
||||||
|
"transaction_date": [["fiscal year", "2023-2024"]],
|
||||||
|
"item_code": args.item_code,
|
||||||
|
"per_billed": [["<", 100]],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ " - "
|
||||||
|
+ frappe.bold(fmt_money(args.ordered_amount, currency=currency))
|
||||||
|
+ "</li></ul>"
|
||||||
|
)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def get_actions(args, budget):
|
def get_actions(args, budget):
|
||||||
yearly_action = budget.action_if_annual_budget_exceeded
|
yearly_action = budget.action_if_annual_budget_exceeded
|
||||||
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
||||||
@@ -265,31 +363,15 @@ def get_actions(args, budget):
|
|||||||
return yearly_action, monthly_action
|
return yearly_action, monthly_action
|
||||||
|
|
||||||
|
|
||||||
def get_amount(args, budget):
|
def get_requested_amount(args):
|
||||||
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)
|
|
||||||
)
|
|
||||||
|
|
||||||
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")
|
item_code = args.get("item_code")
|
||||||
condition = get_other_condition(args, budget, "Material Request")
|
condition = get_other_condition(args, "Material Request")
|
||||||
|
|
||||||
data = frappe.db.sql(
|
data = frappe.db.sql(
|
||||||
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
|
""" 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
|
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
|
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {} and
|
||||||
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(
|
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition),
|
||||||
condition
|
|
||||||
),
|
|
||||||
item_code,
|
item_code,
|
||||||
as_list=1,
|
as_list=1,
|
||||||
)
|
)
|
||||||
@@ -297,17 +379,15 @@ def get_requested_amount(args, budget):
|
|||||||
return data[0][0] if data else 0
|
return data[0][0] if data else 0
|
||||||
|
|
||||||
|
|
||||||
def get_ordered_amount(args, budget):
|
def get_ordered_amount(args):
|
||||||
item_code = args.get("item_code")
|
item_code = args.get("item_code")
|
||||||
condition = get_other_condition(args, budget, "Purchase Order")
|
condition = get_other_condition(args, "Purchase Order")
|
||||||
|
|
||||||
data = frappe.db.sql(
|
data = frappe.db.sql(
|
||||||
""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
f""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
|
||||||
from `tabPurchase Order Item` child, `tabPurchase Order` parent where
|
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
|
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(
|
and parent.status != 'Closed' and {condition}""",
|
||||||
condition
|
|
||||||
),
|
|
||||||
item_code,
|
item_code,
|
||||||
as_list=1,
|
as_list=1,
|
||||||
)
|
)
|
||||||
@@ -315,12 +395,12 @@ def get_ordered_amount(args, budget):
|
|||||||
return data[0][0] if data else 0
|
return data[0][0] if data else 0
|
||||||
|
|
||||||
|
|
||||||
def get_other_condition(args, budget, for_doc):
|
def get_other_condition(args, for_doc):
|
||||||
condition = "expense_account = '%s'" % (args.expense_account)
|
condition = "expense_account = '%s'" % (args.expense_account)
|
||||||
budget_against_field = args.get("budget_against_field")
|
budget_against_field = args.get("budget_against_field")
|
||||||
|
|
||||||
if budget_against_field and args.get(budget_against_field):
|
if budget_against_field and args.get(budget_against_field):
|
||||||
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
|
condition += f" and child.{budget_against_field} = '{args.get(budget_against_field)}'"
|
||||||
|
|
||||||
if args.get("fiscal_year"):
|
if args.get("fiscal_year"):
|
||||||
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
|
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
|
||||||
@@ -328,12 +408,8 @@ def get_other_condition(args, budget, for_doc):
|
|||||||
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
||||||
)
|
)
|
||||||
|
|
||||||
condition += """ and parent.%s
|
condition += f""" and parent.{date_field}
|
||||||
between '%s' and '%s' """ % (
|
between '{start_date}' and '{end_date}' """
|
||||||
date_field,
|
|
||||||
start_date,
|
|
||||||
end_date,
|
|
||||||
)
|
|
||||||
|
|
||||||
return condition
|
return condition
|
||||||
|
|
||||||
@@ -352,21 +428,17 @@ def get_actual_expense(args):
|
|||||||
|
|
||||||
args.update(lft_rgt)
|
args.update(lft_rgt)
|
||||||
|
|
||||||
condition2 = """and exists(select name from `tab{doctype}`
|
condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
|
||||||
where lft>=%(lft)s and rgt<=%(rgt)s
|
where lft>=%(lft)s and rgt<=%(rgt)s
|
||||||
and name=gle.{budget_against_field})""".format(
|
and name=gle.{budget_against_field})"""
|
||||||
doctype=args.budget_against_doctype, budget_against_field=budget_against_field # nosec
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
condition2 = """and exists(select name from `tab{doctype}`
|
condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
|
||||||
where name=gle.{budget_against} and
|
where name=gle.{budget_against_field} and
|
||||||
gle.{budget_against} = %({budget_against})s)""".format(
|
gle.{budget_against_field} = %({budget_against_field})s)"""
|
||||||
doctype=args.budget_against_doctype, budget_against=budget_against_field
|
|
||||||
)
|
|
||||||
|
|
||||||
amount = flt(
|
amount = flt(
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
"""
|
f"""
|
||||||
select sum(gle.debit) - sum(gle.credit)
|
select sum(gle.debit) - sum(gle.credit)
|
||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
where
|
where
|
||||||
@@ -377,9 +449,7 @@ def get_actual_expense(args):
|
|||||||
and gle.company=%(company)s
|
and gle.company=%(company)s
|
||||||
and gle.docstatus=1
|
and gle.docstatus=1
|
||||||
{condition2}
|
{condition2}
|
||||||
""".format(
|
""",
|
||||||
condition1=condition1, condition2=condition2
|
|
||||||
),
|
|
||||||
(args),
|
(args),
|
||||||
)[0][0]
|
)[0][0]
|
||||||
) # nosec
|
) # nosec
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
|
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -63,9 +61,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
|
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -97,9 +93,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||||
|
|
||||||
mr = frappe.get_doc(
|
mr = frappe.get_doc(
|
||||||
@@ -138,9 +132,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
|
||||||
|
|
||||||
po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
|
po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
|
||||||
@@ -158,9 +150,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
|
|
||||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||||
|
|
||||||
@@ -223,7 +213,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
if month > 9:
|
if month > 9:
|
||||||
month = 9
|
month = 9
|
||||||
|
|
||||||
for i in range(month + 1):
|
for _i in range(month + 1):
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _TC",
|
"_Test Bank - _TC",
|
||||||
@@ -237,9 +227,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
||||||
)
|
)
|
||||||
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertRaises(BudgetError, jv.cancel)
|
self.assertRaises(BudgetError, jv.cancel)
|
||||||
|
|
||||||
@@ -255,7 +243,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
month = 9
|
month = 9
|
||||||
|
|
||||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||||
for i in range(month + 1):
|
for _i in range(month + 1):
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _TC",
|
"_Test Bank - _TC",
|
||||||
@@ -270,9 +258,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
|
||||||
)
|
)
|
||||||
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertRaises(BudgetError, jv.cancel)
|
self.assertRaises(BudgetError, jv.cancel)
|
||||||
|
|
||||||
@@ -284,9 +270,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
|
set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
|
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -316,9 +300,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
).insert(ignore_permissions=True)
|
).insert(ignore_permissions=True)
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
|
budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
|
||||||
frappe.db.set_value(
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
|
|
||||||
)
|
|
||||||
|
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Account Cost for Goods Sold - _TC",
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
@@ -423,13 +405,11 @@ def make_budget(**args):
|
|||||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||||
|
|
||||||
if budget_against == "Project":
|
if budget_against == "Project":
|
||||||
project_name = "{0}%".format("_Test Project/" + fiscal_year)
|
project_name = "{}%".format("_Test Project/" + fiscal_year)
|
||||||
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
|
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
|
||||||
else:
|
else:
|
||||||
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
|
cost_center_name = "{}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
|
||||||
budget_list = frappe.get_all(
|
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", cost_center_name)})
|
||||||
"Budget", fields=["name"], filters={"name": ("like", cost_center_name)}
|
|
||||||
)
|
|
||||||
for d in budget_list:
|
for d in budget_list:
|
||||||
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
|
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
|
||||||
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
|
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
|
||||||
@@ -451,24 +431,18 @@ def make_budget(**args):
|
|||||||
budget.action_if_annual_budget_exceeded = "Stop"
|
budget.action_if_annual_budget_exceeded = "Stop"
|
||||||
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
|
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
|
||||||
budget.budget_against = budget_against
|
budget.budget_against = budget_against
|
||||||
budget.append(
|
budget.append("accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000})
|
||||||
"accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000}
|
|
||||||
)
|
|
||||||
|
|
||||||
if args.applicable_on_material_request:
|
if args.applicable_on_material_request:
|
||||||
budget.applicable_on_material_request = 1
|
budget.applicable_on_material_request = 1
|
||||||
budget.action_if_annual_budget_exceeded_on_mr = (
|
budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or "Warn"
|
||||||
args.action_if_annual_budget_exceeded_on_mr or "Warn"
|
|
||||||
)
|
|
||||||
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
|
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
|
||||||
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
|
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.applicable_on_purchase_order:
|
if args.applicable_on_purchase_order:
|
||||||
budget.applicable_on_purchase_order = 1
|
budget.applicable_on_purchase_order = 1
|
||||||
budget.action_if_annual_budget_exceeded_on_po = (
|
budget.action_if_annual_budget_exceeded_on_po = args.action_if_annual_budget_exceeded_on_po or "Warn"
|
||||||
args.action_if_annual_budget_exceeded_on_po or "Warn"
|
|
||||||
)
|
|
||||||
budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
|
budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
|
||||||
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
|
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,94 +1,42 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-05-16 11:54:09.286135",
|
"creation": "2016-05-16 11:54:09.286135",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"account",
|
||||||
|
"budget_amount"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Account",
|
"label": "Account",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 1
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "budget_amount",
|
"fieldname": "budget_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Budget Amount",
|
"label": "Budget Amount",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2017-01-02 17:02:53.339420",
|
"modified": "2024-03-04 15:43:27.016947",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Budget Account",
|
"name": "Budget Account",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_seen": 0
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cash Flow Mapper', {
|
frappe.ui.form.on("Cash Flow Mapper", {});
|
||||||
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,43 +1,45 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cash Flow Mapping', {
|
frappe.ui.form.on("Cash Flow Mapping", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frm.events.disable_unchecked_fields(frm);
|
frm.events.disable_unchecked_fields(frm);
|
||||||
},
|
},
|
||||||
reset_check_fields: function(frm) {
|
reset_check_fields: function (frm) {
|
||||||
frm.fields.filter(field => field.df.fieldtype === 'Check')
|
frm.fields
|
||||||
.map(field => frm.set_df_property(field.df.fieldname, 'read_only', 0));
|
.filter((field) => field.df.fieldtype === "Check")
|
||||||
|
.map((field) => frm.set_df_property(field.df.fieldname, "read_only", 0));
|
||||||
},
|
},
|
||||||
has_checked_field(frm) {
|
has_checked_field(frm) {
|
||||||
const val = frm.fields.filter(field => field.value === 1);
|
const val = frm.fields.filter((field) => field.value === 1);
|
||||||
return val.length ? 1 : 0;
|
return val.length ? 1 : 0;
|
||||||
},
|
},
|
||||||
_disable_unchecked_fields: function(frm) {
|
_disable_unchecked_fields: function (frm) {
|
||||||
// get value of clicked field
|
// get value of clicked field
|
||||||
frm.fields.filter(field => field.value === 0)
|
frm.fields
|
||||||
.map(field => frm.set_df_property(field.df.fieldname, 'read_only', 1));
|
.filter((field) => field.value === 0)
|
||||||
|
.map((field) => frm.set_df_property(field.df.fieldname, "read_only", 1));
|
||||||
},
|
},
|
||||||
disable_unchecked_fields: function(frm) {
|
disable_unchecked_fields: function (frm) {
|
||||||
frm.events.reset_check_fields(frm);
|
frm.events.reset_check_fields(frm);
|
||||||
const checked = frm.events.has_checked_field(frm);
|
const checked = frm.events.has_checked_field(frm);
|
||||||
if (checked) {
|
if (checked) {
|
||||||
frm.events._disable_unchecked_fields(frm);
|
frm.events._disable_unchecked_fields(frm);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
is_working_capital: function(frm) {
|
is_working_capital: function (frm) {
|
||||||
frm.events.disable_unchecked_fields(frm);
|
frm.events.disable_unchecked_fields(frm);
|
||||||
},
|
},
|
||||||
is_finance_cost: function(frm) {
|
is_finance_cost: function (frm) {
|
||||||
frm.events.disable_unchecked_fields(frm);
|
frm.events.disable_unchecked_fields(frm);
|
||||||
},
|
},
|
||||||
is_income_tax_liability: function(frm) {
|
is_income_tax_liability: function (frm) {
|
||||||
frm.events.disable_unchecked_fields(frm);
|
frm.events.disable_unchecked_fields(frm);
|
||||||
},
|
},
|
||||||
is_income_tax_expense: function(frm) {
|
is_income_tax_expense: function (frm) {
|
||||||
frm.events.disable_unchecked_fields(frm);
|
frm.events.disable_unchecked_fields(frm);
|
||||||
},
|
},
|
||||||
is_finance_cost_adjustment: function(frm) {
|
is_finance_cost_adjustment: function (frm) {
|
||||||
frm.events.disable_unchecked_fields(frm);
|
frm.events.disable_unchecked_fields(frm);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cash Flow Mapping Template', {
|
frappe.ui.form.on("Cash Flow Mapping Template", {});
|
||||||
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cash Flow Mapping Template Details', {
|
frappe.ui.form.on("Cash Flow Mapping Template Details", {});
|
||||||
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cashier Closing', {
|
frappe.ui.form.on("Cashier Closing", {
|
||||||
|
setup: function (frm) {
|
||||||
setup: function(frm){
|
|
||||||
if (frm.doc.user == "" || frm.doc.user == null) {
|
if (frm.doc.user == "" || frm.doc.user == null) {
|
||||||
frm.doc.user = frappe.session.user;
|
frm.doc.user = frappe.session.user;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,457 +1,152 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-06-18 16:51:49.994750",
|
"creation": "2018-06-18 16:51:49.994750",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"naming_series",
|
||||||
|
"user",
|
||||||
|
"date",
|
||||||
|
"from_time",
|
||||||
|
"time",
|
||||||
|
"expense",
|
||||||
|
"custody",
|
||||||
|
"returns",
|
||||||
|
"outstanding_amount",
|
||||||
|
"payments",
|
||||||
|
"net_amount",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "POS-CLO-",
|
"default": "POS-CLO-",
|
||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Series",
|
"label": "Series",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "POS-CLO-",
|
"options": "POS-CLO-",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "user",
|
"fieldname": "user",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "User",
|
"label": "User",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "User",
|
"options": "User",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
"reqd": 1
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Today",
|
"default": "Today",
|
||||||
"fieldname": "date",
|
"fieldname": "date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Date",
|
"label": "Date",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "from_time",
|
"fieldname": "from_time",
|
||||||
"fieldtype": "Time",
|
"fieldtype": "Time",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "From Time",
|
"label": "From Time",
|
||||||
"length": 0,
|
"reqd": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "",
|
|
||||||
"fieldname": "time",
|
"fieldname": "time",
|
||||||
"fieldtype": "Time",
|
"fieldtype": "Time",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "To Time",
|
"label": "To Time",
|
||||||
"length": 0,
|
"reqd": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0.00",
|
"default": "0.00",
|
||||||
"fieldname": "expense",
|
"fieldname": "expense",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
"label": "Expense"
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Expense",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0.00",
|
"default": "0.00",
|
||||||
"fieldname": "custody",
|
"fieldname": "custody",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
"label": "Custody"
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Custody",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0.00",
|
"default": "0.00",
|
||||||
"fieldname": "returns",
|
"fieldname": "returns",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Returns",
|
"label": "Returns",
|
||||||
"length": 0,
|
"precision": "2"
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "2",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0.00",
|
"default": "0.00",
|
||||||
"fieldname": "outstanding_amount",
|
"fieldname": "outstanding_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Outstanding Amount",
|
"label": "Outstanding Amount",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0.0",
|
|
||||||
"fieldname": "payments",
|
"fieldname": "payments",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Payments",
|
"label": "Payments",
|
||||||
"length": 0,
|
"options": "Cashier Closing Payments"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Cashier Closing Payments",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "net_amount",
|
"fieldname": "net_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Net Amount",
|
"label": "Net Amount",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amended From",
|
"label": "Amended From",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Cashier Closing",
|
"options": "Cashier Closing",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"links": [],
|
||||||
"istable": 0,
|
"modified": "2023-12-28 13:15:46.858427",
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-02-19 08:35:24.157327",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Cashier Closing",
|
"name": "Cashier Closing",
|
||||||
"name_case": "",
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"states": [],
|
||||||
"track_seen": 0,
|
"track_changes": 1
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
frappe.ui.form.on('Chart of Accounts Importer', {
|
frappe.ui.form.on("Chart of Accounts Importer", {
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
frm.set_value("company", "");
|
frm.set_value("company", "");
|
||||||
frm.set_value("import_file", "");
|
frm.set_value("import_file", "");
|
||||||
@@ -8,31 +8,34 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
frm.disable_save();
|
frm.disable_save();
|
||||||
|
|
||||||
// make company mandatory
|
// make company mandatory
|
||||||
frm.set_df_property('company', 'reqd', frm.doc.company ? 0 : 1);
|
frm.set_df_property("company", "reqd", frm.doc.company ? 0 : 1);
|
||||||
frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1);
|
frm.set_df_property("import_file_section", "hidden", frm.doc.company ? 0 : 1);
|
||||||
|
|
||||||
if (frm.doc.import_file) {
|
if (frm.doc.import_file) {
|
||||||
frappe.run_serially([
|
frappe.run_serially([
|
||||||
() => generate_tree_preview(frm),
|
() => generate_tree_preview(frm),
|
||||||
() => create_import_button(frm),
|
() => create_import_button(frm),
|
||||||
() => frm.set_df_property('chart_preview', 'hidden', 0)
|
() => frm.set_df_property("chart_preview", "hidden", 0),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.set_df_property('chart_preview', 'hidden',
|
frm.set_df_property(
|
||||||
$(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1);
|
"chart_preview",
|
||||||
|
"hidden",
|
||||||
|
$(frm.fields_dict["chart_tree"].wrapper).html() != "" ? 0 : 1
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
download_template: function(frm) {
|
download_template: function (frm) {
|
||||||
var d = new frappe.ui.Dialog({
|
var d = new frappe.ui.Dialog({
|
||||||
title: __("Download Template"),
|
title: __("Download Template"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
label : "File Type",
|
label: "File Type",
|
||||||
fieldname: "file_type",
|
fieldname: "file_type",
|
||||||
fieldtype: "Select",
|
fieldtype: "Select",
|
||||||
reqd: 1,
|
reqd: 1,
|
||||||
options: ["Excel", "CSV"]
|
options: ["Excel", "CSV"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Template Type",
|
label: "Template Type",
|
||||||
@@ -41,21 +44,27 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
reqd: 1,
|
reqd: 1,
|
||||||
options: ["Sample Template", "Blank Template"],
|
options: ["Sample Template", "Blank Template"],
|
||||||
change: () => {
|
change: () => {
|
||||||
let template_type = d.get_value('template_type');
|
let template_type = d.get_value("template_type");
|
||||||
|
|
||||||
if (template_type === "Sample Template") {
|
if (template_type === "Sample Template") {
|
||||||
d.set_df_property('template_type', 'description',
|
d.set_df_property(
|
||||||
|
"template_type",
|
||||||
|
"description",
|
||||||
`The Sample Template contains all the required accounts pre filled in the template.
|
`The Sample Template contains all the required accounts pre filled in the template.
|
||||||
You can add more accounts or change existing accounts in the template as per your choice.`);
|
You can add more accounts or change existing accounts in the template as per your choice.`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
d.set_df_property('template_type', 'description',
|
d.set_df_property(
|
||||||
|
"template_type",
|
||||||
|
"description",
|
||||||
`The Blank Template contains just the account type and root type required to build the Chart
|
`The Blank Template contains just the account type and root type required to build the Chart
|
||||||
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
of Accounts. Please enter the account names and add more rows as per your requirement.`
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label : "Company",
|
label: "Company",
|
||||||
fieldname: "company",
|
fieldname: "company",
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
reqd: 1,
|
reqd: 1,
|
||||||
@@ -63,25 +72,25 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
default: frm.doc.company,
|
default: frm.doc.company,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function () {
|
||||||
let data = d.get_values();
|
let data = d.get_values();
|
||||||
|
|
||||||
if (!data.template_type) {
|
if (!data.template_type) {
|
||||||
frappe.throw(__('Please select <b>Template Type</b> to download template'));
|
frappe.throw(__("Please select <b>Template Type</b> to download template"));
|
||||||
}
|
}
|
||||||
|
|
||||||
open_url_post(
|
open_url_post(
|
||||||
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
|
"/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template",
|
||||||
{
|
{
|
||||||
file_type: data.file_type,
|
file_type: data.file_type,
|
||||||
template_type: data.template_type,
|
template_type: data.template_type,
|
||||||
company: data.company
|
company: data.company,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
d.hide();
|
d.hide();
|
||||||
},
|
},
|
||||||
primary_action_label: __('Download')
|
primary_action_label: __("Download"),
|
||||||
});
|
});
|
||||||
d.show();
|
d.show();
|
||||||
},
|
},
|
||||||
@@ -89,7 +98,7 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
import_file: function (frm) {
|
import_file: function (frm) {
|
||||||
if (!frm.doc.import_file) {
|
if (!frm.doc.import_file) {
|
||||||
frm.page.set_indicator("");
|
frm.page.set_indicator("");
|
||||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file
|
$(frm.fields_dict["chart_tree"].wrapper).empty(); // empty wrapper on removing file
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -99,89 +108,97 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company",
|
||||||
args: {
|
args: {
|
||||||
company: frm.doc.company
|
company: frm.doc.company,
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if(r.message===false) {
|
if (r.message === false) {
|
||||||
frm.set_value("company", "");
|
frm.set_value("company", "");
|
||||||
frappe.throw(__("Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions."));
|
frappe.throw(
|
||||||
|
__(
|
||||||
|
"Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions."
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
frm.trigger("refresh");
|
frm.trigger("refresh");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var create_import_button = function(frm) {
|
var create_import_button = function (frm) {
|
||||||
frm.page.set_primary_action(__("Import"), function () {
|
frm.page
|
||||||
|
.set_primary_action(__("Import"), function () {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||||
args: {
|
args: {
|
||||||
file_name: frm.doc.import_file,
|
file_name: frm.doc.import_file,
|
||||||
company: frm.doc.company
|
company: frm.doc.company,
|
||||||
},
|
},
|
||||||
freeze: true,
|
freeze: true,
|
||||||
freeze_message: __("Creating Accounts..."),
|
freeze_message: __("Creating Accounts..."),
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if (!r.exc) {
|
if (!r.exc) {
|
||||||
clearInterval(frm.page["interval"]);
|
clearInterval(frm.page["interval"]);
|
||||||
frm.page.set_indicator(__('Import Successful'), 'blue');
|
frm.page.set_indicator(__("Import Successful"), "blue");
|
||||||
create_reset_button(frm);
|
create_reset_button(frm);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}).addClass('btn btn-primary');
|
})
|
||||||
|
.addClass("btn btn-primary");
|
||||||
};
|
};
|
||||||
|
|
||||||
var create_reset_button = function(frm) {
|
var create_reset_button = function (frm) {
|
||||||
frm.page.set_primary_action(__("Reset"), function () {
|
frm.page
|
||||||
|
.set_primary_action(__("Reset"), function () {
|
||||||
frm.page.clear_primary_action();
|
frm.page.clear_primary_action();
|
||||||
delete frm.page["show_import_button"];
|
delete frm.page["show_import_button"];
|
||||||
frm.reload_doc();
|
frm.reload_doc();
|
||||||
}).addClass('btn btn-primary');
|
})
|
||||||
|
.addClass("btn btn-primary");
|
||||||
};
|
};
|
||||||
|
|
||||||
var validate_coa = function(frm) {
|
var validate_coa = function (frm) {
|
||||||
if (frm.doc.import_file) {
|
if (frm.doc.import_file) {
|
||||||
let parent = __('All Accounts');
|
let parent = __("All Accounts");
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa",
|
||||||
'args': {
|
|
||||||
file_name: frm.doc.import_file,
|
|
||||||
parent: parent,
|
|
||||||
doctype: 'Chart of Accounts Importer',
|
|
||||||
file_type: frm.doc.file_type,
|
|
||||||
for_validate: 1
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if (r.message['show_import_button']) {
|
|
||||||
frm.page['show_import_button'] = Boolean(r.message['show_import_button']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var generate_tree_preview = function(frm) {
|
|
||||||
let parent = __('All Accounts');
|
|
||||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
|
|
||||||
|
|
||||||
// generate tree structure based on the csv data
|
|
||||||
return new frappe.ui.Tree({
|
|
||||||
parent: $(frm.fields_dict['chart_tree'].wrapper),
|
|
||||||
label: parent,
|
|
||||||
expandable: true,
|
|
||||||
method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
|
||||||
args: {
|
args: {
|
||||||
file_name: frm.doc.import_file,
|
file_name: frm.doc.import_file,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
doctype: 'Chart of Accounts Importer',
|
doctype: "Chart of Accounts Importer",
|
||||||
file_type: frm.doc.file_type
|
file_type: frm.doc.file_type,
|
||||||
|
for_validate: 1,
|
||||||
},
|
},
|
||||||
onclick: function(node) {
|
callback: function (r) {
|
||||||
parent = node.value;
|
if (r.message["show_import_button"]) {
|
||||||
|
frm.page["show_import_button"] = Boolean(r.message["show_import_button"]);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var generate_tree_preview = function (frm) {
|
||||||
|
let parent = __("All Accounts");
|
||||||
|
$(frm.fields_dict["chart_tree"].wrapper).empty(); // empty wrapper to load new data
|
||||||
|
|
||||||
|
// generate tree structure based on the csv data
|
||||||
|
return new frappe.ui.Tree({
|
||||||
|
parent: $(frm.fields_dict["chart_tree"].wrapper),
|
||||||
|
label: parent,
|
||||||
|
expandable: true,
|
||||||
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa",
|
||||||
|
args: {
|
||||||
|
file_name: frm.doc.import_file,
|
||||||
|
parent: parent,
|
||||||
|
doctype: "Chart of Accounts Importer",
|
||||||
|
file_type: frm.doc.file_type,
|
||||||
|
},
|
||||||
|
onclick: function (node) {
|
||||||
|
parent = node.value;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import
|
|||||||
class ChartofAccountsImporter(Document):
|
class ChartofAccountsImporter(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.import_file:
|
if self.import_file:
|
||||||
get_coa(
|
get_coa("Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1)
|
||||||
"Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_columns(data):
|
def validate_columns(data):
|
||||||
@@ -104,7 +102,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
|
|||||||
file_path = file_doc.get_full_path()
|
file_path = file_doc.get_full_path()
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
with open(file_path, "r") as in_file:
|
with open(file_path) as in_file:
|
||||||
csv_reader = list(csv.reader(in_file))
|
csv_reader = list(csv.reader(in_file))
|
||||||
headers = csv_reader[0]
|
headers = csv_reader[0]
|
||||||
del csv_reader[0] # delete top row and headers row
|
del csv_reader[0] # delete top row and headers row
|
||||||
@@ -203,10 +201,10 @@ def build_forest(data):
|
|||||||
for row in data:
|
for row in data:
|
||||||
account_name, parent_account, account_number, parent_account_number = row[0:4]
|
account_name, parent_account, account_number, parent_account_number = row[0:4]
|
||||||
if account_number:
|
if account_number:
|
||||||
account_name = "{} - {}".format(account_number, account_name)
|
account_name = f"{account_number} - {account_name}"
|
||||||
if parent_account_number:
|
if parent_account_number:
|
||||||
parent_account_number = cstr(parent_account_number).strip()
|
parent_account_number = cstr(parent_account_number).strip()
|
||||||
parent_account = "{} - {}".format(parent_account_number, parent_account)
|
parent_account = f"{parent_account_number} - {parent_account}"
|
||||||
|
|
||||||
if parent_account == account_name == child:
|
if parent_account == account_name == child:
|
||||||
return [parent_account]
|
return [parent_account]
|
||||||
@@ -218,7 +216,7 @@ def build_forest(data):
|
|||||||
frappe.bold(parent_account)
|
frappe.bold(parent_account)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return [child] + parent_account_list
|
return [child, *parent_account_list]
|
||||||
|
|
||||||
charts_map, paths = {}, []
|
charts_map, paths = {}, []
|
||||||
|
|
||||||
@@ -238,12 +236,12 @@ def build_forest(data):
|
|||||||
) = i
|
) = i
|
||||||
|
|
||||||
if not account_name:
|
if not account_name:
|
||||||
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
|
error_messages.append(f"Row {line_no}: Please enter Account Name")
|
||||||
|
|
||||||
name = account_name
|
name = account_name
|
||||||
if account_number:
|
if account_number:
|
||||||
account_number = cstr(account_number).strip()
|
account_number = cstr(account_number).strip()
|
||||||
account_name = "{} - {}".format(account_number, account_name)
|
account_name = f"{account_number} - {account_name}"
|
||||||
|
|
||||||
charts_map[account_name] = {}
|
charts_map[account_name] = {}
|
||||||
charts_map[account_name]["account_name"] = name
|
charts_map[account_name]["account_name"] = name
|
||||||
@@ -340,9 +338,9 @@ def get_template(template_type, company):
|
|||||||
|
|
||||||
def get_sample_template(writer, company):
|
def get_sample_template(writer, company):
|
||||||
currency = frappe.db.get_value("Company", company, "default_currency")
|
currency = frappe.db.get_value("Company", company, "default_currency")
|
||||||
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f:
|
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv")) as f:
|
||||||
for row in f:
|
for row in f:
|
||||||
row = row.strip().split(",") + [currency]
|
row = [*row.strip().split(","), currency]
|
||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
return writer
|
return writer
|
||||||
@@ -451,7 +449,7 @@ def unset_existing_data(company):
|
|||||||
"Purchase Taxes and Charges Template",
|
"Purchase Taxes and Charges Template",
|
||||||
]:
|
]:
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
'''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company) # nosec
|
f'''delete from `tab{doctype}` where `company`="%s"''' % (company) # nosec
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,20 @@
|
|||||||
|
|
||||||
frappe.provide("erpnext.cheque_print");
|
frappe.provide("erpnext.cheque_print");
|
||||||
|
|
||||||
frappe.ui.form.on('Cheque Print Template', {
|
frappe.ui.form.on("Cheque Print Template", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
if(!frm.doc.__islocal) {
|
if (!frm.doc.__islocal) {
|
||||||
frm.add_custom_button(frm.doc.has_print_format?__("Update Print Format"):__("Create Print Format"),
|
frm.add_custom_button(
|
||||||
function() {
|
frm.doc.has_print_format ? __("Update Print Format") : __("Create Print Format"),
|
||||||
|
function () {
|
||||||
erpnext.cheque_print.view_cheque_print(frm);
|
erpnext.cheque_print.view_cheque_print(frm);
|
||||||
}).addClass("btn-primary");
|
}
|
||||||
|
).addClass("btn-primary");
|
||||||
|
|
||||||
$(frm.fields_dict.cheque_print_preview.wrapper).empty()
|
$(frm.fields_dict.cheque_print_preview.wrapper).empty();
|
||||||
|
|
||||||
|
var template =
|
||||||
var template = '<div style="position: relative; overflow-x: scroll;">\
|
'<div style="position: relative; overflow-x: scroll;">\
|
||||||
<div id="cheque_preview" style="width: {{ cheque_width }}cm; \
|
<div id="cheque_preview" style="width: {{ cheque_width }}cm; \
|
||||||
height: {{ cheque_height }}cm;\
|
height: {{ cheque_height }}cm;\
|
||||||
background-repeat: no-repeat;\
|
background-repeat: no-repeat;\
|
||||||
@@ -48,30 +50,30 @@ frappe.ui.form.on('Cheque Print Template', {
|
|||||||
</div>\
|
</div>\
|
||||||
</div>';
|
</div>';
|
||||||
|
|
||||||
$(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper)
|
$(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper);
|
||||||
|
|
||||||
if (frm.doc.scanned_cheque) {
|
if (frm.doc.scanned_cheque) {
|
||||||
$(frm.fields_dict.cheque_print_preview.wrapper).find("#cheque_preview").css('background-image', 'url(' + frm.doc.scanned_cheque + ')');
|
$(frm.fields_dict.cheque_print_preview.wrapper)
|
||||||
}
|
.find("#cheque_preview")
|
||||||
|
.css("background-image", "url(" + frm.doc.scanned_cheque + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
erpnext.cheque_print.view_cheque_print = function (frm) {
|
||||||
erpnext.cheque_print.view_cheque_print = function(frm) {
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.cheque_print_template.cheque_print_template.create_or_update_cheque_print_format",
|
method: "erpnext.accounts.doctype.cheque_print_template.cheque_print_template.create_or_update_cheque_print_format",
|
||||||
args:{
|
args: {
|
||||||
"template_name": frm.doc.name
|
template_name: frm.doc.name,
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if (!r.exe && !frm.doc.has_print_format) {
|
if (!r.exe && !frm.doc.has_print_format) {
|
||||||
var doc = frappe.model.sync(r.message);
|
var doc = frappe.model.sync(r.message);
|
||||||
frappe.set_route("Form", r.message.doctype, r.message.name);
|
frappe.set_route("Form", r.message.doctype, r.message.name);
|
||||||
|
} else {
|
||||||
|
frappe.msgprint(__("Print settings updated in respective print format"));
|
||||||
}
|
}
|
||||||
else {
|
},
|
||||||
frappe.msgprint(__("Print settings updated in respective print format"))
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,71 +31,71 @@ def create_or_update_cheque_print_format(template_name):
|
|||||||
|
|
||||||
cheque_print.html = """
|
cheque_print.html = """
|
||||||
<style>
|
<style>
|
||||||
.print-format {
|
.print-format {{
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}}
|
||||||
@media screen {
|
@media screen {{
|
||||||
.print-format {
|
.print-format {{
|
||||||
padding: 0in;
|
padding: 0in;
|
||||||
}
|
}}
|
||||||
}
|
}}
|
||||||
</style>
|
</style>
|
||||||
<div style="position: relative; top:%(starting_position_from_top_edge)scm">
|
<div style="position: relative; top:{starting_position_from_top_edge}cm">
|
||||||
<div style="width:%(cheque_width)scm;height:%(cheque_height)scm;">
|
<div style="width:{cheque_width}cm;height:{cheque_height}cm;">
|
||||||
<span style="top:%(acc_pay_dist_from_top_edge)scm; left:%(acc_pay_dist_from_left_edge)scm;
|
<span style="top:{acc_pay_dist_from_top_edge}cm; left:{acc_pay_dist_from_left_edge}cm;
|
||||||
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
|
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
|
||||||
%(message_to_show)s
|
{message_to_show}
|
||||||
</span>
|
</span>
|
||||||
<span style="top:%(date_dist_from_top_edge)scm; left:%(date_dist_from_left_edge)scm;
|
<span style="top:{date_dist_from_top_edge}cm; left:{date_dist_from_left_edge}cm;
|
||||||
position: absolute;">
|
position: absolute;">
|
||||||
{{ frappe.utils.formatdate(doc.reference_date) or '' }}
|
{{{{ frappe.utils.formatdate(doc.reference_date) or '' }}}}
|
||||||
</span>
|
</span>
|
||||||
<span style="top:%(acc_no_dist_from_top_edge)scm;left:%(acc_no_dist_from_left_edge)scm;
|
<span style="top:{acc_no_dist_from_top_edge}cm;left:{acc_no_dist_from_left_edge}cm;
|
||||||
position: absolute; min-width: 6cm;">
|
position: absolute; min-width: 6cm;">
|
||||||
{{ doc.account_no or '' }}
|
{{{{ doc.account_no or '' }}}}
|
||||||
</span>
|
</span>
|
||||||
<span style="top:%(payer_name_from_top_edge)scm;left: %(payer_name_from_left_edge)scm;
|
<span style="top:{payer_name_from_top_edge}cm;left: {payer_name_from_left_edge}cm;
|
||||||
position: absolute; min-width: 6cm;">
|
position: absolute; min-width: 6cm;">
|
||||||
{{doc.party_name}}
|
{{{{doc.party_name}}}}
|
||||||
</span>
|
</span>
|
||||||
<span style="top:%(amt_in_words_from_top_edge)scm; left:%(amt_in_words_from_left_edge)scm;
|
<span style="top:{amt_in_words_from_top_edge}cm; left:{amt_in_words_from_left_edge}cm;
|
||||||
position: absolute; display: block; width: %(amt_in_word_width)scm;
|
position: absolute; display: block; width: {amt_in_word_width}cm;
|
||||||
line-height:%(amt_in_words_line_spacing)scm; word-wrap: break-word;">
|
line-height:{amt_in_words_line_spacing}cm; word-wrap: break-word;">
|
||||||
{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}
|
{{{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}}}
|
||||||
</span>
|
</span>
|
||||||
<span style="top:%(amt_in_figures_from_top_edge)scm;left: %(amt_in_figures_from_left_edge)scm;
|
<span style="top:{amt_in_figures_from_top_edge}cm;left: {amt_in_figures_from_left_edge}cm;
|
||||||
position: absolute; min-width: 4cm;">
|
position: absolute; min-width: 4cm;">
|
||||||
{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}
|
{{{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}}}
|
||||||
</span>
|
</span>
|
||||||
<span style="top:%(signatory_from_top_edge)scm;left: %(signatory_from_left_edge)scm;
|
<span style="top:{signatory_from_top_edge}cm;left: {signatory_from_left_edge}cm;
|
||||||
position: absolute; min-width: 6cm;">
|
position: absolute; min-width: 6cm;">
|
||||||
{{doc.company}}
|
{{{{doc.company}}}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>""" % {
|
</div>""".format(
|
||||||
"starting_position_from_top_edge": doc.starting_position_from_top_edge
|
starting_position_from_top_edge=doc.starting_position_from_top_edge
|
||||||
if doc.cheque_size == "A4"
|
if doc.cheque_size == "A4"
|
||||||
else 0.0,
|
else 0.0,
|
||||||
"cheque_width": doc.cheque_width,
|
cheque_width=doc.cheque_width,
|
||||||
"cheque_height": doc.cheque_height,
|
cheque_height=doc.cheque_height,
|
||||||
"acc_pay_dist_from_top_edge": doc.acc_pay_dist_from_top_edge,
|
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,
|
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"),
|
message_to_show=doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
|
||||||
"date_dist_from_top_edge": doc.date_dist_from_top_edge,
|
date_dist_from_top_edge=doc.date_dist_from_top_edge,
|
||||||
"date_dist_from_left_edge": doc.date_dist_from_left_edge,
|
date_dist_from_left_edge=doc.date_dist_from_left_edge,
|
||||||
"acc_no_dist_from_top_edge": doc.acc_no_dist_from_top_edge,
|
acc_no_dist_from_top_edge=doc.acc_no_dist_from_top_edge,
|
||||||
"acc_no_dist_from_left_edge": doc.acc_no_dist_from_left_edge,
|
acc_no_dist_from_left_edge=doc.acc_no_dist_from_left_edge,
|
||||||
"payer_name_from_top_edge": doc.payer_name_from_top_edge,
|
payer_name_from_top_edge=doc.payer_name_from_top_edge,
|
||||||
"payer_name_from_left_edge": doc.payer_name_from_left_edge,
|
payer_name_from_left_edge=doc.payer_name_from_left_edge,
|
||||||
"amt_in_words_from_top_edge": doc.amt_in_words_from_top_edge,
|
amt_in_words_from_top_edge=doc.amt_in_words_from_top_edge,
|
||||||
"amt_in_words_from_left_edge": doc.amt_in_words_from_left_edge,
|
amt_in_words_from_left_edge=doc.amt_in_words_from_left_edge,
|
||||||
"amt_in_word_width": doc.amt_in_word_width,
|
amt_in_word_width=doc.amt_in_word_width,
|
||||||
"amt_in_words_line_spacing": doc.amt_in_words_line_spacing,
|
amt_in_words_line_spacing=doc.amt_in_words_line_spacing,
|
||||||
"amt_in_figures_from_top_edge": doc.amt_in_figures_from_top_edge,
|
amt_in_figures_from_top_edge=doc.amt_in_figures_from_top_edge,
|
||||||
"amt_in_figures_from_left_edge": doc.amt_in_figures_from_left_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_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)
|
cheque_print.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|||||||
@@ -3,75 +3,80 @@
|
|||||||
|
|
||||||
frappe.provide("erpnext.accounts");
|
frappe.provide("erpnext.accounts");
|
||||||
|
|
||||||
|
frappe.ui.form.on("Cost Center", {
|
||||||
|
onload: function (frm) {
|
||||||
frappe.ui.form.on('Cost Center', {
|
frm.set_query("parent_cost_center", function () {
|
||||||
onload: function(frm) {
|
|
||||||
frm.set_query("parent_cost_center", function() {
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
is_group: 1
|
is_group: 1,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
if (!frm.is_new()) {
|
if (!frm.is_new()) {
|
||||||
frm.add_custom_button(__('Update Cost Center Name / Number'), function () {
|
frm.add_custom_button(__("Update Cost Center Name / Number"), function () {
|
||||||
frm.trigger("update_cost_center_number");
|
frm.trigger("update_cost_center_number");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let intro_txt = '';
|
let intro_txt = "";
|
||||||
let doc = frm.doc;
|
let doc = frm.doc;
|
||||||
frm.toggle_display('cost_center_name', doc.__islocal);
|
frm.toggle_display("cost_center_name", doc.__islocal);
|
||||||
frm.toggle_enable(['is_group', 'company'], doc.__islocal);
|
frm.toggle_enable(["is_group", "company"], doc.__islocal);
|
||||||
|
|
||||||
if(!doc.__islocal && doc.is_group==1) {
|
if (!doc.__islocal && doc.is_group == 1) {
|
||||||
intro_txt += __('Note: This Cost Center is a Group. Cannot make accounting entries against groups.');
|
intro_txt += __(
|
||||||
|
"Note: This Cost Center is a Group. Cannot make accounting entries against groups."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.events.hide_unhide_group_ledger(frm);
|
frm.events.hide_unhide_group_ledger(frm);
|
||||||
|
|
||||||
frm.toggle_display('sb1', doc.is_group==0);
|
frm.toggle_display("sb1", doc.is_group == 0);
|
||||||
frm.set_intro(intro_txt);
|
frm.set_intro(intro_txt);
|
||||||
|
|
||||||
if(!frm.doc.__islocal) {
|
if (!frm.doc.__islocal) {
|
||||||
frm.add_custom_button(__('Chart of Cost Centers'),
|
frm.add_custom_button(__("Chart of Cost Centers"), function () {
|
||||||
function() { frappe.set_route("Tree", "Cost Center"); });
|
frappe.set_route("Tree", "Cost Center");
|
||||||
|
});
|
||||||
|
|
||||||
frm.add_custom_button(__('Budget'),
|
frm.add_custom_button(__("Budget"), function () {
|
||||||
function() { frappe.set_route("List", "Budget", {'cost_center': frm.doc.name}); });
|
frappe.set_route("List", "Budget", { cost_center: frm.doc.name });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update_cost_center_number: function(frm) {
|
update_cost_center_number: function (frm) {
|
||||||
var d = new frappe.ui.Dialog({
|
var d = new frappe.ui.Dialog({
|
||||||
title: __('Update Cost Center Name / Number'),
|
title: __("Update Cost Center Name / Number"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"label": "Cost Center Name",
|
label: "Cost Center Name",
|
||||||
"fieldname": "cost_center_name",
|
fieldname: "cost_center_name",
|
||||||
"fieldtype": "Data",
|
fieldtype: "Data",
|
||||||
"reqd": 1,
|
reqd: 1,
|
||||||
"default": frm.doc.cost_center_name
|
default: frm.doc.cost_center_name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Cost Center Number",
|
label: "Cost Center Number",
|
||||||
"fieldname": "cost_center_number",
|
fieldname: "cost_center_number",
|
||||||
"fieldtype": "Data",
|
fieldtype: "Data",
|
||||||
"default": frm.doc.cost_center_number
|
default: frm.doc.cost_center_number,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": __("Merge with existing"),
|
label: __("Merge with existing"),
|
||||||
"fieldname": "merge",
|
fieldname: "merge",
|
||||||
"fieldtype": "Check",
|
fieldtype: "Check",
|
||||||
"default": 0
|
default: 0,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function () {
|
||||||
var data = d.get_values();
|
var data = d.get_values();
|
||||||
if(data.cost_center_name === frm.doc.cost_center_name && data.cost_center_number === frm.doc.cost_center_number) {
|
if (
|
||||||
|
data.cost_center_name === frm.doc.cost_center_name &&
|
||||||
|
data.cost_center_number === frm.doc.cost_center_number
|
||||||
|
) {
|
||||||
d.hide();
|
d.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -83,12 +88,12 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
cost_center_name: data.cost_center_name,
|
cost_center_name: data.cost_center_name,
|
||||||
cost_center_number: cstr(data.cost_center_number),
|
cost_center_number: cstr(data.cost_center_number),
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
merge: data.merge
|
merge: data.merge,
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
frappe.dom.unfreeze();
|
frappe.dom.unfreeze();
|
||||||
if(!r.exc) {
|
if (!r.exc) {
|
||||||
if(r.message) {
|
if (r.message) {
|
||||||
frappe.set_route("Form", "Cost Center", r.message);
|
frappe.set_route("Form", "Cost Center", r.message);
|
||||||
} else {
|
} else {
|
||||||
me.frm.set_value("cost_center_name", data.cost_center_name);
|
me.frm.set_value("cost_center_name", data.cost_center_name);
|
||||||
@@ -96,44 +101,42 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
}
|
}
|
||||||
d.hide();
|
d.hide();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
primary_action_label: __('Update')
|
primary_action_label: __("Update"),
|
||||||
});
|
});
|
||||||
d.show();
|
d.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
parent_cost_center(frm) {
|
parent_cost_center(frm) {
|
||||||
if(!frm.doc.company) {
|
if (!frm.doc.company) {
|
||||||
frappe.msgprint(__('Please enter company name first'));
|
frappe.msgprint(__("Please enter company name first"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hide_unhide_group_ledger(frm) {
|
hide_unhide_group_ledger(frm) {
|
||||||
let doc = frm.doc;
|
let doc = frm.doc;
|
||||||
if (doc.is_group == 1) {
|
if (doc.is_group == 1) {
|
||||||
frm.add_custom_button(__('Convert to Non-Group'),
|
frm.add_custom_button(__("Convert to Non-Group"), () => frm.events.convert_to_ledger(frm));
|
||||||
() => frm.events.convert_to_ledger(frm));
|
|
||||||
} else if (doc.is_group == 0) {
|
} else if (doc.is_group == 0) {
|
||||||
frm.add_custom_button(__('Convert to Group'),
|
frm.add_custom_button(__("Convert to Group"), () => frm.events.convert_to_group(frm));
|
||||||
() => frm.events.convert_to_group(frm));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
convert_to_group(frm) {
|
convert_to_group(frm) {
|
||||||
frm.call('convert_ledger_to_group').then(r => {
|
frm.call("convert_ledger_to_group").then((r) => {
|
||||||
if(r.message === 1) {
|
if (r.message === 1) {
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
convert_to_ledger(frm) {
|
convert_to_ledger(frm) {
|
||||||
frm.call('convert_group_to_ledger').then(r => {
|
frm.call("convert_group_to_ledger").then((r) => {
|
||||||
if(r.message === 1) {
|
if (r.message === 1) {
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-01-31 13:22:58.916273",
|
"modified": "2024-04-24 10:55:54.083042",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Cost Center",
|
"name": "Cost Center",
|
||||||
@@ -163,6 +163,15 @@
|
|||||||
{
|
{
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Purchase User"
|
"role": "Purchase User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Employee",
|
||||||
|
"select": 1,
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "parent_cost_center, is_group",
|
"search_fields": "parent_cost_center, is_group",
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ class CostCenter(NestedSet):
|
|||||||
def autoname(self):
|
def autoname(self):
|
||||||
from erpnext.accounts.utils import get_autoname_with_number
|
from erpnext.accounts.utils import get_autoname_with_number
|
||||||
|
|
||||||
self.name = get_autoname_with_number(
|
self.name = get_autoname_with_number(self.cost_center_number, self.cost_center_name, self.company)
|
||||||
self.cost_center_number, self.cost_center_name, self.company
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
@@ -90,14 +88,14 @@ class CostCenter(NestedSet):
|
|||||||
new_cost_center = get_name_with_abbr(newdn, self.company)
|
new_cost_center = get_name_with_abbr(newdn, self.company)
|
||||||
|
|
||||||
# Validate properties before merging
|
# Validate properties before merging
|
||||||
super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "is_group")
|
super().before_rename(olddn, new_cost_center, merge, "is_group")
|
||||||
if not merge:
|
if not merge:
|
||||||
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
|
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
|
||||||
|
|
||||||
return new_cost_center
|
return new_cost_center
|
||||||
|
|
||||||
def after_rename(self, olddn, newdn, merge=False):
|
def after_rename(self, olddn, newdn, merge=False):
|
||||||
super(CostCenter, self).after_rename(olddn, newdn, merge)
|
super().after_rename(olddn, newdn, merge)
|
||||||
|
|
||||||
if not merge:
|
if not merge:
|
||||||
new_cost_center = frappe.db.get_value(
|
new_cost_center = frappe.db.get_value(
|
||||||
|
|||||||
@@ -1,54 +1,84 @@
|
|||||||
frappe.treeview_settings["Cost Center"] = {
|
frappe.treeview_settings["Cost Center"] = {
|
||||||
breadcrumb: "Accounts",
|
breadcrumb: "Accounts",
|
||||||
get_tree_root: false,
|
get_tree_root: false,
|
||||||
filters: [{
|
filters: [
|
||||||
|
{
|
||||||
fieldname: "company",
|
fieldname: "company",
|
||||||
fieldtype:"Select",
|
fieldtype: "Select",
|
||||||
options: erpnext.utils.get_tree_options("company"),
|
options: erpnext.utils.get_tree_options("company"),
|
||||||
label: __("Company"),
|
label: __("Company"),
|
||||||
default: erpnext.utils.get_tree_default("company")
|
default: erpnext.utils.get_tree_default("company"),
|
||||||
}],
|
},
|
||||||
|
],
|
||||||
root_label: "Cost Centers",
|
root_label: "Cost Centers",
|
||||||
get_tree_nodes: 'erpnext.accounts.utils.get_children',
|
get_tree_nodes: "erpnext.accounts.utils.get_children",
|
||||||
add_tree_node: 'erpnext.accounts.utils.add_cc',
|
add_tree_node: "erpnext.accounts.utils.add_cc",
|
||||||
menu_items:[
|
menu_items: [
|
||||||
{
|
{
|
||||||
label: __('New Company'),
|
label: __("New Company"),
|
||||||
action: function() { frappe.new_doc("Company", true) },
|
action: function () {
|
||||||
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1'
|
frappe.new_doc("Company", true);
|
||||||
}
|
},
|
||||||
|
condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
fields:[
|
fields: [
|
||||||
{fieldtype:'Data', fieldname:'cost_center_name', label:__('New Cost Center Name'), reqd:true},
|
{ fieldtype: "Data", fieldname: "cost_center_name", label: __("New Cost Center Name"), reqd: true },
|
||||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
{
|
||||||
description:__('Further cost centers can be made under Groups but entries can be made against non-Groups')},
|
fieldtype: "Check",
|
||||||
{fieldtype:'Data', fieldname:'cost_center_number', label:__('Cost Center Number'),
|
fieldname: "is_group",
|
||||||
description: __("Number of new Cost Center, it will be included in the cost center name as a prefix")}
|
label: __("Is Group"),
|
||||||
|
description: __(
|
||||||
|
"Further cost centers can be made under Groups but entries can be made against non-Groups"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Data",
|
||||||
|
fieldname: "cost_center_number",
|
||||||
|
label: __("Cost Center Number"),
|
||||||
|
description: __(
|
||||||
|
"Number of new Cost Center, it will be included in the cost center name as a prefix"
|
||||||
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
ignore_fields:["parent_cost_center"],
|
ignore_fields: ["parent_cost_center"],
|
||||||
onload: function(treeview) {
|
onload: function (treeview) {
|
||||||
function get_company() {
|
function get_company() {
|
||||||
return treeview.page.fields_dict.company.get_value();
|
return treeview.page.fields_dict.company.get_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
treeview.page.add_inner_button(__("Chart of Accounts"), function() {
|
treeview.page.add_inner_button(
|
||||||
frappe.set_route('Tree', 'Account', {company: get_company()});
|
__("Chart of Accounts"),
|
||||||
}, __('View'));
|
function () {
|
||||||
|
frappe.set_route("Tree", "Account", { company: get_company() });
|
||||||
|
},
|
||||||
|
__("View")
|
||||||
|
);
|
||||||
|
|
||||||
// make
|
// make
|
||||||
treeview.page.add_inner_button(__("Budget List"), function() {
|
treeview.page.add_inner_button(
|
||||||
frappe.set_route('List', 'Budget', {company: get_company()});
|
__("Budget List"),
|
||||||
}, __('Budget'));
|
function () {
|
||||||
|
frappe.set_route("List", "Budget", { company: get_company() });
|
||||||
|
},
|
||||||
|
__("Budget")
|
||||||
|
);
|
||||||
|
|
||||||
treeview.page.add_inner_button(__("Monthly Distribution"), function() {
|
treeview.page.add_inner_button(
|
||||||
frappe.set_route('List', 'Monthly Distribution', {company: get_company()});
|
__("Monthly Distribution"),
|
||||||
}, __('Budget'));
|
function () {
|
||||||
|
frappe.set_route("List", "Monthly Distribution", { company: get_company() });
|
||||||
|
},
|
||||||
|
__("Budget")
|
||||||
|
);
|
||||||
|
|
||||||
treeview.page.add_inner_button(__("Budget Variance Report"), function() {
|
treeview.page.add_inner_button(
|
||||||
frappe.set_route('query-report', 'Budget Variance Report', {company: get_company()});
|
__("Budget Variance Report"),
|
||||||
}, __('Budget'));
|
function () {
|
||||||
|
frappe.set_route("query-report", "Budget Variance Report", { company: get_company() });
|
||||||
}
|
},
|
||||||
|
__("Budget")
|
||||||
}
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ test_records = frappe.get_test_records("Cost Center")
|
|||||||
|
|
||||||
class TestCostCenter(unittest.TestCase):
|
class TestCostCenter(unittest.TestCase):
|
||||||
def test_cost_center_creation_against_child_node(self):
|
def test_cost_center_creation_against_child_node(self):
|
||||||
|
|
||||||
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
|
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
|
||||||
frappe.get_doc(test_records[1]).insert()
|
frappe.get_doc(test_records[1]).insert()
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cost Center Allocation', {
|
frappe.ui.form.on("Cost Center Allocation", {
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
let filters = {"is_group": 0};
|
frm.set_query("main_cost_center", function () {
|
||||||
if (frm.doc.company) {
|
|
||||||
$.extend(filters, {
|
|
||||||
"company": frm.doc.company
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
frm.set_query('main_cost_center', function() {
|
|
||||||
return {
|
return {
|
||||||
filters: filters
|
filters: {
|
||||||
|
company: frm.doc.company,
|
||||||
|
is_group: 0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
frm.set_query("cost_center", "allocation_percentages", function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company,
|
||||||
|
is_group: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class InvalidDateError(frappe.ValidationError):
|
|||||||
|
|
||||||
class CostCenterAllocation(Document):
|
class CostCenterAllocation(Document):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(CostCenterAllocation, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._skip_from_date_validation = False
|
self._skip_from_date_validation = False
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -44,9 +44,7 @@ class CostCenterAllocation(Document):
|
|||||||
total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
|
total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
|
||||||
|
|
||||||
if total_percentage != 100:
|
if total_percentage != 100:
|
||||||
frappe.throw(
|
frappe.throw(_("Total percentage against cost centers should be 100"), WrongPercentageAllocation)
|
||||||
_("Total percentage against cost centers should be 100"), WrongPercentageAllocation
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_from_date_based_on_existing_gle(self):
|
def validate_from_date_based_on_existing_gle(self):
|
||||||
# Check if GLE exists against the main cost center
|
# Check if GLE exists against the main cost center
|
||||||
|
|||||||
@@ -1,44 +1,41 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Coupon Code', {
|
frappe.ui.form.on("Coupon Code", {
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("pricing_rule", function() {
|
frm.set_query("pricing_rule", function () {
|
||||||
return {
|
return {
|
||||||
filters: [
|
filters: [["Pricing Rule", "coupon_code_based", "=", "1"]],
|
||||||
["Pricing Rule","coupon_code_based", "=", "1"]
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
coupon_name:function(frm){
|
coupon_name: function (frm) {
|
||||||
if (frm.doc.__islocal===1) {
|
if (frm.doc.__islocal === 1) {
|
||||||
frm.trigger("make_coupon_code");
|
frm.trigger("make_coupon_code");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
coupon_type:function(frm){
|
coupon_type: function (frm) {
|
||||||
if (frm.doc.__islocal===1) {
|
if (frm.doc.__islocal === 1) {
|
||||||
frm.trigger("make_coupon_code");
|
frm.trigger("make_coupon_code");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
make_coupon_code: function(frm) {
|
make_coupon_code: function (frm) {
|
||||||
var coupon_name=frm.doc.coupon_name;
|
var coupon_name = frm.doc.coupon_name;
|
||||||
var coupon_code;
|
var coupon_code;
|
||||||
if (frm.doc.coupon_type=='Gift Card') {
|
if (frm.doc.coupon_type == "Gift Card") {
|
||||||
coupon_code=Math.random().toString(12).substring(2, 12).toUpperCase();
|
coupon_code = Math.random().toString(12).substring(2, 12).toUpperCase();
|
||||||
|
} else if (frm.doc.coupon_type == "Promotional") {
|
||||||
|
coupon_name = coupon_name.replace(/\s/g, "");
|
||||||
|
coupon_code = coupon_name.toUpperCase().slice(0, 8);
|
||||||
}
|
}
|
||||||
else if(frm.doc.coupon_type=='Promotional'){
|
frm.doc.coupon_code = coupon_code;
|
||||||
coupon_name=coupon_name.replace(/\s/g,'');
|
frm.refresh_field("coupon_code");
|
||||||
coupon_code=coupon_name.toUpperCase().slice(0,8);
|
|
||||||
}
|
|
||||||
frm.doc.coupon_code=coupon_code;
|
|
||||||
frm.refresh_field('coupon_code');
|
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
if (frm.doc.pricing_rule) {
|
if (frm.doc.pricing_rule) {
|
||||||
frm.add_custom_button(__("Add/Edit Coupon Conditions"), function(){
|
frm.add_custom_button(__("Add/Edit Coupon Conditions"), function () {
|
||||||
frappe.set_route("Form", "Pricing Rule", frm.doc.pricing_rule);
|
frappe.set_route("Form", "Pricing Rule", frm.doc.pricing_rule);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +1,41 @@
|
|||||||
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Currency Exchange Settings', {
|
frappe.ui.form.on("Currency Exchange Settings", {
|
||||||
service_provider: function(frm) {
|
service_provider: function (frm) {
|
||||||
|
frm.call({
|
||||||
|
method: "erpnext.accounts.doctype.currency_exchange_settings.currency_exchange_settings.get_api_endpoint",
|
||||||
|
args: {
|
||||||
|
service_provider: frm.doc.service_provider,
|
||||||
|
use_http: frm.doc.use_http,
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
if (r && r.message) {
|
||||||
if (frm.doc.service_provider == "exchangerate.host") {
|
if (frm.doc.service_provider == "exchangerate.host") {
|
||||||
let result = ['result'];
|
let result = ["result"];
|
||||||
let params = {
|
let params = {
|
||||||
date: '{transaction_date}',
|
date: "{transaction_date}",
|
||||||
from: '{from_currency}',
|
from: "{from_currency}",
|
||||||
to: '{to_currency}'
|
to: "{to_currency}",
|
||||||
};
|
};
|
||||||
add_param(frm, "https://api.exchangerate.host/convert", params, result);
|
add_param(frm, r.message, params, result);
|
||||||
} else if (frm.doc.service_provider == "frankfurter.app") {
|
} else if (frm.doc.service_provider == "frankfurter.app") {
|
||||||
let result = ['rates', '{to_currency}'];
|
let result = ["rates", "{to_currency}"];
|
||||||
let params = {
|
let params = {
|
||||||
base: '{from_currency}',
|
base: "{from_currency}",
|
||||||
symbols: '{to_currency}'
|
symbols: "{to_currency}",
|
||||||
};
|
};
|
||||||
add_param(frm, "https://frankfurter.app/{transaction_date}", params, result);
|
add_param(frm, r.message, params, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
use_http: function (frm) {
|
||||||
|
frm.trigger("service_provider");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function add_param(frm, api, params, result) {
|
function add_param(frm, api, params, result) {
|
||||||
var row;
|
var row;
|
||||||
frm.clear_table("req_params");
|
frm.clear_table("req_params");
|
||||||
@@ -30,13 +43,13 @@ function add_param(frm, api, params, result) {
|
|||||||
|
|
||||||
frm.doc.api_endpoint = api;
|
frm.doc.api_endpoint = api;
|
||||||
|
|
||||||
$.each(params, function(key, value) {
|
$.each(params, function (key, value) {
|
||||||
row = frm.add_child("req_params");
|
row = frm.add_child("req_params");
|
||||||
row.key = key;
|
row.key = key;
|
||||||
row.value = value;
|
row.value = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
$.each(result, function(key, value) {
|
$.each(result, function (key, value) {
|
||||||
row = frm.add_child("result_key");
|
row = frm.add_child("result_key");
|
||||||
row.key = value;
|
row.key = value;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"disabled",
|
"disabled",
|
||||||
"service_provider",
|
"service_provider",
|
||||||
"api_endpoint",
|
"api_endpoint",
|
||||||
|
"use_http",
|
||||||
"access_key",
|
"access_key",
|
||||||
"url",
|
"url",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
@@ -91,12 +92,19 @@
|
|||||||
"fieldname": "access_key",
|
"fieldname": "access_key",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Access Key"
|
"label": "Access Key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval: doc.service_provider != \"Custom\"",
|
||||||
|
"fieldname": "use_http",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Use HTTP Protocol"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-10-04 15:30:25.333860",
|
"modified": "2024-03-18 08:32:26.895076",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Currency Exchange Settings",
|
"name": "Currency Exchange Settings",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ class CurrencyExchangeSettings(Document):
|
|||||||
|
|
||||||
def set_parameters_and_result(self):
|
def set_parameters_and_result(self):
|
||||||
if self.service_provider == "exchangerate.host":
|
if self.service_provider == "exchangerate.host":
|
||||||
|
|
||||||
if not self.access_key:
|
if not self.access_key:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Access Key is required for Service Provider: {0}").format(
|
_("Access Key is required for Service Provider: {0}").format(
|
||||||
@@ -29,7 +28,7 @@ class CurrencyExchangeSettings(Document):
|
|||||||
self.set("result_key", [])
|
self.set("result_key", [])
|
||||||
self.set("req_params", [])
|
self.set("req_params", [])
|
||||||
|
|
||||||
self.api_endpoint = "https://api.exchangerate.host/convert"
|
self.api_endpoint = get_api_endpoint(self.service_provider, self.use_http)
|
||||||
self.append("result_key", {"key": "result"})
|
self.append("result_key", {"key": "result"})
|
||||||
self.append("req_params", {"key": "access_key", "value": self.access_key})
|
self.append("req_params", {"key": "access_key", "value": self.access_key})
|
||||||
self.append("req_params", {"key": "amount", "value": "1"})
|
self.append("req_params", {"key": "amount", "value": "1"})
|
||||||
@@ -40,7 +39,7 @@ class CurrencyExchangeSettings(Document):
|
|||||||
self.set("result_key", [])
|
self.set("result_key", [])
|
||||||
self.set("req_params", [])
|
self.set("req_params", [])
|
||||||
|
|
||||||
self.api_endpoint = "https://frankfurter.app/{transaction_date}"
|
self.api_endpoint = get_api_endpoint(self.service_provider, self.use_http)
|
||||||
self.append("result_key", {"key": "rates"})
|
self.append("result_key", {"key": "rates"})
|
||||||
self.append("result_key", {"key": "{to_currency}"})
|
self.append("result_key", {"key": "{to_currency}"})
|
||||||
self.append("req_params", {"key": "base", "value": "{from_currency}"})
|
self.append("req_params", {"key": "base", "value": "{from_currency}"})
|
||||||
@@ -53,9 +52,7 @@ class CurrencyExchangeSettings(Document):
|
|||||||
transaction_date=nowdate(), to_currency="INR", from_currency="USD"
|
transaction_date=nowdate(), to_currency="INR", from_currency="USD"
|
||||||
)
|
)
|
||||||
|
|
||||||
api_url = self.api_endpoint.format(
|
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:
|
try:
|
||||||
response = requests.get(api_url, params=params)
|
response = requests.get(api_url, params=params)
|
||||||
@@ -75,7 +72,23 @@ class CurrencyExchangeSettings(Document):
|
|||||||
]
|
]
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(_("Invalid result key. Response:") + " " + response.text)
|
frappe.throw(_("Invalid result key. Response:") + " " + response.text)
|
||||||
if not isinstance(value, (int, float)):
|
if not isinstance(value, int | float):
|
||||||
frappe.throw(_("Returned exchange rate is neither integer not float."))
|
frappe.throw(_("Returned exchange rate is neither integer not float."))
|
||||||
|
|
||||||
self.url = response.url
|
self.url = response.url
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_api_endpoint(service_provider: str | None = None, use_http: bool = False):
|
||||||
|
if service_provider and service_provider in ["exchangerate.host", "frankfurter.app"]:
|
||||||
|
if service_provider == "exchangerate.host":
|
||||||
|
api = "api.exchangerate.host/convert"
|
||||||
|
elif service_provider == "frankfurter.app":
|
||||||
|
api = "frankfurter.app/{transaction_date}"
|
||||||
|
|
||||||
|
protocol = "https://"
|
||||||
|
if use_http:
|
||||||
|
protocol = "http://"
|
||||||
|
|
||||||
|
return protocol + api
|
||||||
|
return None
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ frappe.ui.form.on("Dunning", {
|
|||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
outstanding_amount: [">", 0],
|
outstanding_amount: [">", 0],
|
||||||
status: "Overdue"
|
status: "Overdue",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -18,18 +18,14 @@ frappe.ui.form.on("Dunning", {
|
|||||||
filters: {
|
filters: {
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
root_type: "Income",
|
root_type: "Income",
|
||||||
is_group: 0
|
is_group: 0,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
frm.set_df_property("company", "read_only", frm.doc.__islocal ? 0 : 1);
|
frm.set_df_property("company", "read_only", frm.doc.__islocal ? 0 : 1);
|
||||||
frm.set_df_property(
|
frm.set_df_property("sales_invoice", "read_only", frm.doc.__islocal ? 0 : 1);
|
||||||
"sales_invoice",
|
|
||||||
"read_only",
|
|
||||||
frm.doc.__islocal ? 0 : 1
|
|
||||||
);
|
|
||||||
if (frm.doc.docstatus === 1 && frm.doc.status === "Unresolved") {
|
if (frm.doc.docstatus === 1 && frm.doc.status === "Unresolved") {
|
||||||
frm.add_custom_button(__("Resolve"), () => {
|
frm.add_custom_button(__("Resolve"), () => {
|
||||||
frm.set_value("status", "Resolved");
|
frm.set_value("status", "Resolved");
|
||||||
@@ -40,22 +36,27 @@ frappe.ui.form.on("Dunning", {
|
|||||||
__("Payment"),
|
__("Payment"),
|
||||||
function () {
|
function () {
|
||||||
frm.events.make_payment_entry(frm);
|
frm.events.make_payment_entry(frm);
|
||||||
},__("Create")
|
},
|
||||||
|
__("Create")
|
||||||
);
|
);
|
||||||
frm.page.set_inner_btn_group_as_primary(__("Create"));
|
frm.page.set_inner_btn_group_as_primary(__("Create"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(frm.doc.docstatus > 0) {
|
if (frm.doc.docstatus > 0) {
|
||||||
frm.add_custom_button(__('Ledger'), function() {
|
frm.add_custom_button(
|
||||||
|
__("Ledger"),
|
||||||
|
function () {
|
||||||
frappe.route_options = {
|
frappe.route_options = {
|
||||||
"voucher_no": frm.doc.name,
|
voucher_no: frm.doc.name,
|
||||||
"from_date": frm.doc.posting_date,
|
from_date: frm.doc.posting_date,
|
||||||
"to_date": frm.doc.posting_date,
|
to_date: frm.doc.posting_date,
|
||||||
"company": frm.doc.company,
|
company: frm.doc.company,
|
||||||
"show_cancelled_entries": frm.doc.docstatus === 2
|
show_cancelled_entries: frm.doc.docstatus === 2,
|
||||||
};
|
};
|
||||||
frappe.set_route("query-report", "General Ledger");
|
frappe.set_route("query-report", "General Ledger");
|
||||||
}, __('View'));
|
},
|
||||||
|
__("View")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
overdue_days: function (frm) {
|
overdue_days: function (frm) {
|
||||||
@@ -86,8 +87,7 @@ frappe.ui.form.on("Dunning", {
|
|||||||
get_dunning_letter_text: function (frm) {
|
get_dunning_letter_text: function (frm) {
|
||||||
if (frm.doc.dunning_type) {
|
if (frm.doc.dunning_type) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:
|
method: "erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text",
|
||||||
"erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text",
|
|
||||||
args: {
|
args: {
|
||||||
dunning_type: frm.doc.dunning_type,
|
dunning_type: frm.doc.dunning_type,
|
||||||
language: frm.doc.language,
|
language: frm.doc.language,
|
||||||
@@ -129,26 +129,25 @@ frappe.ui.form.on("Dunning", {
|
|||||||
},
|
},
|
||||||
calculate_overdue_days: function (frm) {
|
calculate_overdue_days: function (frm) {
|
||||||
if (frm.doc.posting_date && frm.doc.due_date) {
|
if (frm.doc.posting_date && frm.doc.due_date) {
|
||||||
const overdue_days = moment(frm.doc.posting_date).diff(
|
const overdue_days = moment(frm.doc.posting_date).diff(frm.doc.due_date, "days");
|
||||||
frm.doc.due_date,
|
|
||||||
"days"
|
|
||||||
);
|
|
||||||
frm.set_value("overdue_days", overdue_days);
|
frm.set_value("overdue_days", overdue_days);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calculate_interest_and_amount: function (frm) {
|
calculate_interest_and_amount: function (frm) {
|
||||||
const interest_per_year = frm.doc.outstanding_amount * frm.doc.rate_of_interest / 100;
|
const interest_per_year = (frm.doc.outstanding_amount * frm.doc.rate_of_interest) / 100;
|
||||||
const interest_amount = flt((interest_per_year * cint(frm.doc.overdue_days)) / 365 || 0, precision('interest_amount'));
|
const interest_amount = flt(
|
||||||
const dunning_amount = flt(interest_amount + frm.doc.dunning_fee, precision('dunning_amount'));
|
(interest_per_year * cint(frm.doc.overdue_days)) / 365 || 0,
|
||||||
const grand_total = flt(frm.doc.outstanding_amount + dunning_amount, precision('grand_total'));
|
precision("interest_amount")
|
||||||
|
);
|
||||||
|
const dunning_amount = flt(interest_amount + frm.doc.dunning_fee, precision("dunning_amount"));
|
||||||
|
const grand_total = flt(frm.doc.outstanding_amount + dunning_amount, precision("grand_total"));
|
||||||
frm.set_value("interest_amount", interest_amount);
|
frm.set_value("interest_amount", interest_amount);
|
||||||
frm.set_value("dunning_amount", dunning_amount);
|
frm.set_value("dunning_amount", dunning_amount);
|
||||||
frm.set_value("grand_total", grand_total);
|
frm.set_value("grand_total", grand_total);
|
||||||
},
|
},
|
||||||
make_payment_entry: function (frm) {
|
make_payment_entry: function (frm) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method:
|
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
|
||||||
"erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
|
|
||||||
args: {
|
args: {
|
||||||
dt: frm.doc.doctype,
|
dt: frm.doc.doctype,
|
||||||
dn: frm.doc.name,
|
dn: frm.doc.name,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Dunning Type', {
|
frappe.ui.form.on("Dunning Type", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,75 +1,79 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Exchange Rate Revaluation', {
|
frappe.ui.form.on("Exchange Rate Revaluation", {
|
||||||
setup: function(frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("party_type", "accounts", function() {
|
frm.set_query("party_type", "accounts", function () {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
filters: {
|
||||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
name: ["in", Object.keys(frappe.boot.party_account_types)],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
frm.set_query("account", "accounts", function(doc) {
|
frm.set_query("account", "accounts", function (doc) {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
filters: {
|
||||||
"company": doc.company
|
company: doc.company,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
if(frm.doc.docstatus==1) {
|
if (frm.doc.docstatus == 1) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'check_journal_entry_condition',
|
method: "check_journal_entry_condition",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r) {
|
callback: function (r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
frm.add_custom_button(__('Journal Entries'), function() {
|
frm.add_custom_button(
|
||||||
|
__("Journal Entries"),
|
||||||
|
function () {
|
||||||
return frm.events.make_jv(frm);
|
return frm.events.make_jv(frm);
|
||||||
}, __('Create'));
|
},
|
||||||
}
|
__("Create")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
validate_rounding_loss: function(frm) {
|
validate_rounding_loss: function (frm) {
|
||||||
let allowance = frm.doc.rounding_loss_allowance;
|
let allowance = frm.doc.rounding_loss_allowance;
|
||||||
if (!(allowance >= 0 && allowance < 1)) {
|
if (!(allowance >= 0 && allowance < 1)) {
|
||||||
frappe.throw(__("Rounding Loss Allowance should be between 0 and 1"));
|
frappe.throw(__("Rounding Loss Allowance should be between 0 and 1"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
rounding_loss_allowance: function(frm) {
|
rounding_loss_allowance: function (frm) {
|
||||||
frm.events.validate_rounding_loss(frm);
|
frm.events.validate_rounding_loss(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
validate: function(frm) {
|
validate: function (frm) {
|
||||||
frm.events.validate_rounding_loss(frm);
|
frm.events.validate_rounding_loss(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
get_entries: function(frm, account) {
|
get_entries: function (frm, account) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "get_accounts_data",
|
method: "get_accounts_data",
|
||||||
doc: cur_frm.doc,
|
doc: cur_frm.doc,
|
||||||
account: account,
|
account: account,
|
||||||
callback: function(r){
|
callback: function (r) {
|
||||||
frappe.model.clear_table(frm.doc, "accounts");
|
frappe.model.clear_table(frm.doc, "accounts");
|
||||||
if(r.message) {
|
if (r.message) {
|
||||||
r.message.forEach((d) => {
|
r.message.forEach((d) => {
|
||||||
cur_frm.add_child("accounts",d);
|
cur_frm.add_child("accounts", d);
|
||||||
});
|
});
|
||||||
frm.events.get_total_gain_loss(frm);
|
frm.events.get_total_gain_loss(frm);
|
||||||
refresh_field("accounts");
|
refresh_field("accounts");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
get_total_gain_loss: function(frm) {
|
get_total_gain_loss: function (frm) {
|
||||||
if(!(frm.doc.accounts && frm.doc.accounts.length)) return;
|
if (!(frm.doc.accounts && frm.doc.accounts.length)) return;
|
||||||
|
|
||||||
let total_gain_loss = 0;
|
let total_gain_loss = 0;
|
||||||
frm.doc.accounts.forEach((d) => {
|
frm.doc.accounts.forEach((d) => {
|
||||||
@@ -80,7 +84,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
frm.refresh_fields();
|
frm.refresh_fields();
|
||||||
},
|
},
|
||||||
|
|
||||||
make_jv : function(frm) {
|
make_jv: function (frm) {
|
||||||
let revaluation_journal = null;
|
let revaluation_journal = null;
|
||||||
let zero_balance_journal = null;
|
let zero_balance_journal = null;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
@@ -88,66 +92,68 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
freeze: true,
|
freeze: true,
|
||||||
freeze_message: "Making Journal Entries...",
|
freeze_message: "Making Journal Entries...",
|
||||||
callback: function(r){
|
callback: function (r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
let response = r.message;
|
let response = r.message;
|
||||||
if(response['revaluation_jv'] || response['zero_balance_jv']) {
|
if (response["revaluation_jv"] || response["zero_balance_jv"]) {
|
||||||
frappe.msgprint(__("Journals have been created"));
|
frappe.msgprint(__("Journals have been created"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Exchange Rate Revaluation Account", {
|
frappe.ui.form.on("Exchange Rate Revaluation Account", {
|
||||||
new_exchange_rate: function(frm, cdt, cdn) {
|
new_exchange_rate: function (frm, cdt, cdn) {
|
||||||
var row = frappe.get_doc(cdt, cdn);
|
var row = frappe.get_doc(cdt, cdn);
|
||||||
row.new_balance_in_base_currency = flt(row.new_exchange_rate * flt(row.balance_in_account_currency),
|
row.new_balance_in_base_currency = flt(
|
||||||
precision("new_balance_in_base_currency", row));
|
row.new_exchange_rate * flt(row.balance_in_account_currency),
|
||||||
|
precision("new_balance_in_base_currency", row)
|
||||||
|
);
|
||||||
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency);
|
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency);
|
||||||
refresh_field("accounts");
|
refresh_field("accounts");
|
||||||
frm.events.get_total_gain_loss(frm);
|
frm.events.get_total_gain_loss(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
account: function(frm, cdt, cdn) {
|
account: function (frm, cdt, cdn) {
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
if (row.account) {
|
if (row.account) {
|
||||||
get_account_details(frm, cdt, cdn);
|
get_account_details(frm, cdt, cdn);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
party: function(frm, cdt, cdn) {
|
party: function (frm, cdt, cdn) {
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
if (row.party && row.account) {
|
if (row.party && row.account) {
|
||||||
get_account_details(frm, cdt, cdn);
|
get_account_details(frm, cdt, cdn);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
accounts_remove: function(frm) {
|
accounts_remove: function (frm) {
|
||||||
frm.events.get_total_gain_loss(frm);
|
frm.events.get_total_gain_loss(frm);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var get_account_details = function(frm, cdt, cdn) {
|
var get_account_details = function (frm, cdt, cdn) {
|
||||||
var row = frappe.get_doc(cdt, cdn);
|
var row = frappe.get_doc(cdt, cdn);
|
||||||
if(!frm.doc.company || !frm.doc.posting_date) {
|
if (!frm.doc.company || !frm.doc.posting_date) {
|
||||||
frappe.throw(__("Please select Company and Posting Date to getting entries"));
|
frappe.throw(__("Please select Company and Posting Date to getting entries"));
|
||||||
}
|
}
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.exchange_rate_revaluation.exchange_rate_revaluation.get_account_details",
|
method: "erpnext.accounts.doctype.exchange_rate_revaluation.exchange_rate_revaluation.get_account_details",
|
||||||
args:{
|
args: {
|
||||||
account: row.account,
|
account: row.account,
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
posting_date: frm.doc.posting_date,
|
posting_date: frm.doc.posting_date,
|
||||||
party_type: row.party_type,
|
party_type: row.party_type,
|
||||||
party: row.party,
|
party: row.party,
|
||||||
rounding_loss_allowance: frm.doc.rounding_loss_allowance
|
rounding_loss_allowance: frm.doc.rounding_loss_allowance,
|
||||||
},
|
},
|
||||||
callback: function(r){
|
callback: function (r) {
|
||||||
$.extend(row, r.message);
|
$.extend(row, r.message);
|
||||||
refresh_field("accounts");
|
refresh_field("accounts");
|
||||||
frm.events.get_total_gain_loss(frm);
|
frm.events.get_total_gain_loss(frm);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -246,7 +246,6 @@ class ExchangeRateRevaluation(Document):
|
|||||||
|
|
||||||
# Handle Accounts with '0' balance in Account/Base Currency
|
# Handle Accounts with '0' balance in Account/Base Currency
|
||||||
for d in [x for x in account_details if x.zero_balance]:
|
for d in [x for x in account_details if x.zero_balance]:
|
||||||
|
|
||||||
if d.balance != 0:
|
if d.balance != 0:
|
||||||
current_exchange_rate = new_exchange_rate = 0
|
current_exchange_rate = new_exchange_rate = 0
|
||||||
|
|
||||||
@@ -259,7 +258,8 @@ class ExchangeRateRevaluation(Document):
|
|||||||
new_balance_in_account_currency = 0
|
new_balance_in_account_currency = 0
|
||||||
|
|
||||||
current_exchange_rate = (
|
current_exchange_rate = (
|
||||||
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0
|
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party)
|
||||||
|
or 0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
gain_loss = new_balance_in_account_currency - (
|
gain_loss = new_balance_in_account_currency - (
|
||||||
@@ -313,9 +313,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
|
|
||||||
revaluation_jv = self.make_jv_for_revaluation()
|
revaluation_jv = self.make_jv_for_revaluation()
|
||||||
if revaluation_jv:
|
if revaluation_jv:
|
||||||
frappe.msgprint(
|
frappe.msgprint(f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}")
|
||||||
f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
|
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
|
||||||
@@ -372,7 +370,8 @@ class ExchangeRateRevaluation(Document):
|
|||||||
journal_account.update(
|
journal_account.update(
|
||||||
{
|
{
|
||||||
dr_or_cr: flt(
|
dr_or_cr: flt(
|
||||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
abs(d.get("balance_in_account_currency")),
|
||||||
|
d.precision("balance_in_account_currency"),
|
||||||
),
|
),
|
||||||
reverse_dr_or_cr: 0,
|
reverse_dr_or_cr: 0,
|
||||||
"debit": 0,
|
"debit": 0,
|
||||||
@@ -498,7 +497,9 @@ class ExchangeRateRevaluation(Document):
|
|||||||
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
|
||||||
),
|
),
|
||||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||||
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
|
"exchange_rate": flt(
|
||||||
|
d.get("current_exchange_rate"), d.precision("current_exchange_rate")
|
||||||
|
),
|
||||||
"reference_type": "Exchange Rate Revaluation",
|
"reference_type": "Exchange Rate Revaluation",
|
||||||
"reference_name": self.name,
|
"reference_name": self.name,
|
||||||
}
|
}
|
||||||
@@ -576,7 +577,7 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_details(
|
def get_account_details(
|
||||||
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float = None
|
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float | None = None
|
||||||
):
|
):
|
||||||
if not (company and posting_date):
|
if not (company and posting_date):
|
||||||
frappe.throw(_("Company and Posting Date is mandatory"))
|
frappe.throw(_("Company and Posting Date is mandatory"))
|
||||||
@@ -589,7 +590,7 @@ def get_account_details(
|
|||||||
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
|
||||||
|
|
||||||
account_details = {}
|
account_details = {}
|
||||||
company_currency = erpnext.get_company_currency(company)
|
erpnext.get_company_currency(company)
|
||||||
|
|
||||||
account_details = {
|
account_details = {
|
||||||
"account_currency": account_currency,
|
"account_currency": account_currency,
|
||||||
@@ -603,12 +604,10 @@ def get_account_details(
|
|||||||
rounding_loss_allowance=rounding_loss_allowance,
|
rounding_loss_allowance=rounding_loss_allowance,
|
||||||
)
|
)
|
||||||
|
|
||||||
if account_balance and (
|
if account_balance and (account_balance[0].balance or account_balance[0].balance_in_account_currency):
|
||||||
account_balance[0].balance or account_balance[0].balance_in_account_currency
|
if account_with_new_balance := ExchangeRateRevaluation.calculate_new_account_balance(
|
||||||
):
|
|
||||||
account_with_new_balance = ExchangeRateRevaluation.calculate_new_account_balance(
|
|
||||||
company, posting_date, account_balance
|
company, posting_date, account_balance
|
||||||
)
|
):
|
||||||
row = account_with_new_balance[0]
|
row = account_with_new_balance[0]
|
||||||
account_details.update(
|
account_details.update(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import qb
|
|
||||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
from frappe.utils import add_days, flt, today
|
from frappe.utils import add_days, flt, today
|
||||||
|
|
||||||
from erpnext import get_default_cost_center
|
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
|
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.accounts.party import get_party_account
|
|
||||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
|
||||||
|
|
||||||
|
|
||||||
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
||||||
@@ -73,9 +66,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
|||||||
err.extend("accounts", accounts)
|
err.extend("accounts", accounts)
|
||||||
row = err.accounts[0]
|
row = err.accounts[0]
|
||||||
row.new_exchange_rate = 85
|
row.new_exchange_rate = 85
|
||||||
row.new_balance_in_base_currency = flt(
|
row.new_balance_in_base_currency = flt(row.new_exchange_rate * flt(row.balance_in_account_currency))
|
||||||
row.new_exchange_rate * flt(row.balance_in_account_currency)
|
|
||||||
)
|
|
||||||
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
|
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
|
||||||
err.set_total_gain_loss()
|
err.set_total_gain_loss()
|
||||||
err = err.save().submit()
|
err = err.save().submit()
|
||||||
@@ -127,9 +118,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
|||||||
pe.save().submit()
|
pe.save().submit()
|
||||||
|
|
||||||
# Cancel the auto created gain/loss JE to simulate balance only in base currency
|
# Cancel the auto created gain/loss JE to simulate balance only in base currency
|
||||||
je = frappe.db.get_all(
|
je = frappe.db.get_all("Journal Entry Account", filters={"reference_name": si.name}, pluck="parent")[
|
||||||
"Journal Entry Account", filters={"reference_name": si.name}, pluck="parent"
|
0
|
||||||
)[0]
|
]
|
||||||
frappe.get_doc("Journal Entry", je).cancel()
|
frappe.get_doc("Journal Entry", je).cancel()
|
||||||
|
|
||||||
err = frappe.new_doc("Exchange Rate Revaluation")
|
err = frappe.new_doc("Exchange Rate Revaluation")
|
||||||
@@ -235,9 +226,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
|||||||
self.assertEqual(flt(acc.debit, precision), 0.0)
|
self.assertEqual(flt(acc.debit, precision), 0.0)
|
||||||
self.assertEqual(flt(acc.credit, precision), 0.0)
|
self.assertEqual(flt(acc.credit, precision), 0.0)
|
||||||
|
|
||||||
row = [x for x in je.accounts if x.account == self.debtors_usd][0]
|
row = next(x for x in je.accounts if x.account == self.debtors_usd)
|
||||||
self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD
|
self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD
|
||||||
row = [x for x in je.accounts if x.account != self.debtors_usd][0]
|
row = next(x for x in je.accounts if x.account != self.debtors_usd)
|
||||||
self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR
|
self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR
|
||||||
|
|
||||||
# total_debit and total_credit will be 0.0, as JV is posting only to account currency fields
|
# total_debit and total_credit will be 0.0, as JV is posting only to account currency fields
|
||||||
@@ -294,5 +285,5 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
|
|||||||
"new_balance_in_account_currency": 100.0,
|
"new_balance_in_account_currency": 100.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, val in expected_data.items():
|
for key, _val in expected_data.items():
|
||||||
self.assertEqual(expected_data.get(key), account_details.get(key))
|
self.assertEqual(expected_data.get(key), account_details.get(key))
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Finance Book', {
|
frappe.ui.form.on("Finance Book", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {},
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Fiscal Year', {
|
frappe.ui.form.on("Fiscal Year", {
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
if(frm.doc.__islocal) {
|
if (frm.doc.__islocal) {
|
||||||
frm.set_value("year_start_date",
|
frm.set_value(
|
||||||
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1));
|
"year_start_date",
|
||||||
|
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
year_start_date: function(frm) {
|
year_start_date: function (frm) {
|
||||||
if (!frm.doc.is_short_year) {
|
if (!frm.doc.is_short_year) {
|
||||||
let year_end_date =
|
let year_end_date = frappe.datetime.add_days(
|
||||||
frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1);
|
frappe.datetime.add_months(frm.doc.year_start_date, 12),
|
||||||
|
-1
|
||||||
|
);
|
||||||
frm.set_value("year_end_date", year_end_date);
|
frm.set_value("year_end_date", year_end_date);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -118,6 +118,14 @@
|
|||||||
{
|
{
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Employee"
|
"role": "Employee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"read": 1,
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"read": 1,
|
||||||
|
"role": "Stock Manager"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
|
|||||||
@@ -98,9 +98,9 @@ class FiscalYear(Document):
|
|||||||
|
|
||||||
if overlap:
|
if overlap:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Year start date or end date is overlapping with {0}. To avoid please set company").format(
|
_(
|
||||||
existing.name
|
"Year start date or end date is overlapping with {0}. To avoid please set company"
|
||||||
),
|
).format(existing.name),
|
||||||
frappe.NameError,
|
frappe.NameError,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,9 +116,9 @@ def check_duplicate_fiscal_year(doc):
|
|||||||
not frappe.flags.in_test
|
not frappe.flags.in_test
|
||||||
):
|
):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(
|
_(
|
||||||
fiscal_year
|
"Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}"
|
||||||
)
|
).format(fiscal_year)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def test_record_generator():
|
|||||||
]
|
]
|
||||||
|
|
||||||
start = 2012
|
start = 2012
|
||||||
end = now_datetime().year + 5
|
end = now_datetime().year + 25
|
||||||
for year in range(start, end):
|
for year in range(start, end):
|
||||||
test_records.append(
|
test_records.append(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('GL Entry', {
|
frappe.ui.form.on("GL Entry", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frm.page.btn_secondary.hide()
|
frm.page.btn_secondary.hide();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user