Merge branch 'version-13-beta-pre-release' of https://github.com/frappe/erpnext into loan_amount_precision_v13

This commit is contained in:
Deepesh Garg 2020-06-07 21:32:50 +05:30
commit 221076c258
631 changed files with 529614 additions and 513998 deletions

View File

@ -1,6 +1,5 @@
dist: trusty
language: python language: python
dist: trusty
git: git:
depth: 1 depth: 1
@ -14,21 +13,10 @@ addons:
jobs: jobs:
include: include:
- name: "Python 2.7 Server Side Test"
python: 2.7
script: bench --site test_site run-tests --app erpnext --coverage
- name: "Python 3.6 Server Side Test" - name: "Python 3.6 Server Side Test"
python: 3.6 python: 3.6
script: bench --site test_site run-tests --app erpnext --coverage script: bench --site test_site run-tests --app erpnext --coverage
- name: "Python 2.7 Patch Test"
python: 2.7
before_script:
- wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
- bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
script: bench --site test_site migrate
- name: "Python 3.6 Patch Test" - name: "Python 3.6 Patch Test"
python: 3.6 python: 3.6
before_script: before_script:
@ -40,8 +28,7 @@ install:
- cd ~ - cd ~
- nvm install 10 - nvm install 10
- git clone https://github.com/frappe/bench --depth 1 - pip install frappe-bench
- pip install -e ./bench
- git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1 - git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
- bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench - bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides from erpnext.hooks import regional_overrides
from frappe.utils import getdate from frappe.utils import getdate
__version__ = '12.0.0-dev' __version__ = '13.0.0-beta.2'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -6,7 +6,7 @@ import frappe, json
from frappe import _ from frappe import _
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form
from erpnext.accounts.report.general_ledger.general_ledger import execute from erpnext.accounts.report.general_ledger.general_ledger import execute
from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan from frappe.utils.dashboard import cache_source, get_from_date_from_timespan
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending
from frappe.utils.nestedset import get_descendants_of from frappe.utils.nestedset import get_descendants_of
@ -14,7 +14,7 @@ from frappe.utils.nestedset import get_descendants_of
@frappe.whitelist() @frappe.whitelist()
@cache_source @cache_source
def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
to_date = None, timespan = None, time_interval = None): to_date = None, timespan = None, time_interval = None, heatmap_year = None):
if chart_name: if chart_name:
chart = frappe.get_doc('Dashboard Chart', chart_name) chart = frappe.get_doc('Dashboard Chart', chart_name)
else: else:

View File

@ -0,0 +1,264 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
import json
from frappe.utils import nowdate, add_months, get_date_str
from frappe import _
from erpnext.accounts.utils import get_fiscal_year, get_account_name
def get_company_for_dashboards():
company = frappe.defaults.get_defaults().company
if company:
return company
else:
company_list = frappe.get_list("Company")
if company_list:
return company_list[0].name
return None
def get_data():
return frappe._dict({
"dashboards": get_dashboards(),
"charts": get_charts(),
"number_cards": get_number_cards()
})
def get_dashboards():
return [{
"name": "Accounts",
"dashboard_name": "Accounts",
"doctype": "Dashboard",
"charts": [
{ "chart": "Profit and Loss" , "width": "Full"},
{ "chart": "Incoming Bills (Purchase Invoice)", "width": "Half"},
{ "chart": "Outgoing Bills (Sales Invoice)", "width": "Half"},
{ "chart": "Accounts Receivable Ageing", "width": "Half"},
{ "chart": "Accounts Payable Ageing", "width": "Half"},
{ "chart": "Budget Variance", "width": "Full"},
{ "chart": "Bank Balance", "width": "Full"}
],
"cards": [
{"card": "Total Outgoing Bills"},
{"card": "Total Incoming Bills"},
{"card": "Total Incoming Payment"},
{"card": "Total Outgoing Payment"}
]
}]
def get_charts():
company = frappe.get_doc("Company", get_company_for_dashboards())
bank_account = company.default_bank_account or get_account_name("Bank", company=company.name)
fiscal_year = get_fiscal_year(date=nowdate())
default_cost_center = company.cost_center
return [
{
"doctype": "Dashboard Charts",
"name": "Profit and Loss",
"owner": "Administrator",
"report_name": "Profit and Loss Statement",
"filters_json": json.dumps({
"company": company.name,
"filter_based_on": "Fiscal Year",
"from_fiscal_year": fiscal_year[0],
"to_fiscal_year": fiscal_year[0],
"periodicity": "Monthly",
"include_default_book_entries": 1
}),
"type": "Bar",
'timeseries': 0,
"chart_type": "Report",
"chart_name": _("Profit and Loss"),
"is_custom": 1,
"is_public": 1
},
{
"doctype": "Dashboard Chart",
"time_interval": "Monthly",
"name": "Incoming Bills (Purchase Invoice)",
"chart_name": _("Incoming Bills (Purchase Invoice)"),
"timespan": "Last Year",
"color": "#a83333",
"value_based_on": "base_net_total",
"filters_json": json.dumps([["Purchase Invoice", "docstatus", "=", 1]]),
"chart_type": "Sum",
"timeseries": 1,
"based_on": "posting_date",
"owner": "Administrator",
"document_type": "Purchase Invoice",
"type": "Bar",
"width": "Half",
"is_public": 1
},
{
"doctype": "Dashboard Chart",
"name": "Outgoing Bills (Sales Invoice)",
"time_interval": "Monthly",
"chart_name": _("Outgoing Bills (Sales Invoice)"),
"timespan": "Last Year",
"color": "#7b933d",
"value_based_on": "base_net_total",
"filters_json": json.dumps([["Sales Invoice", "docstatus", "=", 1]]),
"chart_type": "Sum",
"timeseries": 1,
"based_on": "posting_date",
"owner": "Administrator",
"document_type": "Sales Invoice",
"type": "Bar",
"width": "Half",
"is_public": 1
},
{
"doctype": "Dashboard Charts",
"name": "Accounts Receivable Ageing",
"owner": "Administrator",
"report_name": "Accounts Receivable",
"filters_json": json.dumps({
"company": company.name,
"report_date": nowdate(),
"ageing_based_on": "Due Date",
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120
}),
"type": "Donut",
'timeseries': 0,
"chart_type": "Report",
"chart_name": _("Accounts Receivable Ageing"),
"is_custom": 1,
"is_public": 1
},
{
"doctype": "Dashboard Charts",
"name": "Accounts Payable Ageing",
"owner": "Administrator",
"report_name": "Accounts Payable",
"filters_json": json.dumps({
"company": company.name,
"report_date": nowdate(),
"ageing_based_on": "Due Date",
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120
}),
"type": "Donut",
'timeseries': 0,
"chart_type": "Report",
"chart_name": _("Accounts Payable Ageing"),
"is_custom": 1,
"is_public": 1
},
{
"doctype": "Dashboard Charts",
"name": "Budget Variance",
"owner": "Administrator",
"report_name": "Budget Variance Report",
"filters_json": json.dumps({
"company": company.name,
"from_fiscal_year": fiscal_year[0],
"to_fiscal_year": fiscal_year[0],
"period": "Monthly",
"budget_against": "Cost Center"
}),
"type": "Bar",
"timeseries": 0,
"chart_type": "Report",
"chart_name": _("Budget Variance"),
"is_custom": 1,
"is_public": 1
},
{
"doctype": "Dashboard Charts",
"name": "Bank Balance",
"time_interval": "Quarterly",
"chart_name": "Bank Balance",
"timespan": "Last Year",
"filters_json": json.dumps({
"company": company.name,
"account": bank_account
}),
"source": "Account Balance Timeline",
"chart_type": "Custom",
"timeseries": 1,
"owner": "Administrator",
"type": "Line",
"width": "Half",
"is_public": 1
},
]
def get_number_cards():
fiscal_year = get_fiscal_year(date=nowdate())
year_start_date = get_date_str(fiscal_year[1])
year_end_date = get_date_str(fiscal_year[2])
return [
{
"doctype": "Number Card",
"document_type": "Payment Entry",
"name": "Total Incoming Payment",
"filters_json": json.dumps([
['Payment Entry', 'docstatus', '=', 1],
['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]],
['Payment Entry', 'payment_type', '=', 'Receive']
]),
"label": _("Total Incoming Payment"),
"function": "Sum",
"aggregate_function_based_on": "base_received_amount",
"is_public": 1,
"is_custom": 1,
"show_percentage_stats": 1,
"stats_time_interval": "Monthly"
},
{
"doctype": "Number Card",
"document_type": "Payment Entry",
"name": "Total Outgoing Payment",
"filters_json": json.dumps([
['Payment Entry', 'docstatus', '=', 1],
['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]],
['Payment Entry', 'payment_type', '=', 'Pay']
]),
"label": _("Total Outgoing Payment"),
"function": "Sum",
"aggregate_function_based_on": "base_paid_amount",
"is_public": 1,
"is_custom": 1,
"show_percentage_stats": 1,
"stats_time_interval": "Monthly"
},
{
"doctype": "Number Card",
"document_type": "Sales Invoice",
"name": "Total Outgoing Bills",
"filters_json": json.dumps([
['Sales Invoice', 'docstatus', '=', 1],
['Sales Invoice', 'posting_date', 'between', [year_start_date, year_end_date]]
]),
"label": _("Total Outgoing Bills"),
"function": "Sum",
"aggregate_function_based_on": "base_net_total",
"is_public": 1,
"is_custom": 1,
"show_percentage_stats": 1,
"stats_time_interval": "Monthly"
},
{
"doctype": "Number Card",
"document_type": "Purchase Invoice",
"name": "Total Incoming Bills",
"filters_json": json.dumps([
['Purchase Invoice', 'docstatus', '=', 1],
['Purchase Invoice', 'posting_date', 'between', [year_start_date, year_end_date]]
]),
"label": _("Total Incoming Bills"),
"function": "Sum",
"aggregate_function_based_on": "base_net_total",
"is_public": 1,
"is_custom": 1,
"show_percentage_stats": 1,
"stats_time_interval": "Monthly"
}
]

View File

@ -2,9 +2,10 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, cint, get_link_to_form
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
from frappe.email import sendmail_to_system_managers from frappe.email import sendmail_to_system_managers
from frappe.utils.background_jobs import enqueue
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 '''
@ -32,8 +33,20 @@ def validate_service_stop_date(doc):
if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates.get(item.name): if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates.get(item.name):
frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx)) frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx))
def convert_deferred_expense_to_expense(start_date=None, end_date=None): def build_conditions(process_type, account, company):
conditions=''
deferred_account = "item.deferred_revenue_account" if process_type=="Income" else "item.deferred_expense_account"
if account:
conditions += "AND %s='%s'"%(deferred_account, account)
elif company:
conditions += "AND p.company='%s'"%(company)
return conditions
def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=''):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM # book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date: if not start_date:
start_date = add_months(today(), -1) start_date = add_months(today(), -1)
if not end_date: if not end_date:
@ -41,18 +54,25 @@ def convert_deferred_expense_to_expense(start_date=None, end_date=None):
# 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('''
select distinct parent from `tabPurchase Invoice Item` select distinct item.parent
where service_start_date<=%s and service_end_date>=%s from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
and enable_deferred_expense = 1 and docstatus = 1 and ifnull(amount, 0) > 0 where item.service_start_date<=%s and item.service_end_date>=%s
''', (end_date, start_date)) and item.enable_deferred_expense = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{0}
'''.format(conditions), (end_date, start_date)) #nosec
# For each invoice, book deferred expense # For each invoice, book deferred expense
for invoice in invoices: for invoice in invoices:
doc = frappe.get_doc("Purchase Invoice", invoice) doc = frappe.get_doc("Purchase Invoice", invoice)
book_deferred_income_or_expense(doc, end_date) book_deferred_income_or_expense(doc, deferred_process, end_date)
def convert_deferred_revenue_to_income(start_date=None, end_date=None): if frappe.flags.deferred_accounting_error:
send_mail(deferred_process)
def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=''):
# 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:
start_date = add_months(today(), -1) start_date = add_months(today(), -1)
if not end_date: if not end_date:
@ -60,14 +80,20 @@ def convert_deferred_revenue_to_income(start_date=None, end_date=None):
# 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('''
select distinct parent from `tabSales Invoice Item` select distinct item.parent
where service_start_date<=%s and service_end_date>=%s from `tabSales Invoice Item` item, `tabSales Invoice` p
and enable_deferred_revenue = 1 and docstatus = 1 and ifnull(amount, 0) > 0 where item.service_start_date<=%s and item.service_end_date>=%s
''', (end_date, start_date)) and item.enable_deferred_revenue = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{0}
'''.format(conditions), (end_date, start_date)) #nosec
for invoice in invoices: for invoice in invoices:
doc = frappe.get_doc("Sales Invoice", invoice) doc = frappe.get_doc("Sales Invoice", invoice)
book_deferred_income_or_expense(doc, end_date) book_deferred_income_or_expense(doc, deferred_process, end_date)
if frappe.flags.deferred_accounting_error:
send_mail(deferred_process)
def get_booking_dates(doc, item, posting_date=None): def get_booking_dates(doc, item, posting_date=None):
if not posting_date: if not posting_date:
@ -136,7 +162,7 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
return amount, base_amount return amount, base_amount
def book_deferred_income_or_expense(doc, posting_date=None): def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" \ enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense" if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
@ -159,7 +185,11 @@ def book_deferred_income_or_expense(doc, posting_date=None):
total_days, total_booking_days, account_currency) total_days, total_booking_days, account_currency)
make_gl_entries(doc, credit_account, debit_account, against, make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, end_date, project, account_currency, item.cost_center, item.name) amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error:
return
if getdate(end_date) < getdate(posting_date) and not last_gl_entry: if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
_book_deferred_revenue_or_expense(item) _book_deferred_revenue_or_expense(item)
@ -169,8 +199,33 @@ def book_deferred_income_or_expense(doc, posting_date=None):
if item.get(enable_check): if item.get(enable_check):
_book_deferred_revenue_or_expense(item) _book_deferred_revenue_or_expense(item)
def process_deferred_accounting(posting_date=None):
''' Converts deferred income/expense into income/expense
Executed via background jobs on every month end '''
if not posting_date:
posting_date = today()
if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')):
return
start_date = add_months(today(), -1)
end_date = add_days(today(), -1)
for record_type in ('Income', 'Expense'):
doc = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=posting_date,
start_date=start_date,
end_date=end_date,
type=record_type
))
doc.insert()
doc.submit()
def make_gl_entries(doc, credit_account, debit_account, against, def make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no): amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
# GL Entry for crediting the amount in the deferred expense # GL Entry for crediting the amount in the deferred expense
from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.general_ledger import make_gl_entries
@ -184,10 +239,12 @@ def make_gl_entries(doc, credit_account, debit_account, against,
"credit": base_amount, "credit": base_amount,
"credit_in_account_currency": amount, "credit_in_account_currency": amount,
"cost_center": cost_center, "cost_center": cost_center,
"voucher_detail_no": voucher_detail_no, "voucher_detail_no": item.name,
'posting_date': posting_date, 'posting_date': posting_date,
'project': project 'project': project,
}, account_currency) 'against_voucher_type': 'Process Deferred Accounting',
'against_voucher': deferred_process
}, account_currency, item=item)
) )
# GL Entry to debit the amount from the expense # GL Entry to debit the amount from the expense
gl_entries.append( gl_entries.append(
@ -197,10 +254,12 @@ def make_gl_entries(doc, credit_account, debit_account, against,
"debit": base_amount, "debit": base_amount,
"debit_in_account_currency": amount, "debit_in_account_currency": amount,
"cost_center": cost_center, "cost_center": cost_center,
"voucher_detail_no": voucher_detail_no, "voucher_detail_no": item.name,
'posting_date': posting_date, 'posting_date': posting_date,
'project': project 'project': project,
}, account_currency) 'against_voucher_type': 'Process Deferred Accounting',
'against_voucher': deferred_process
}, account_currency, item=item)
) )
if gl_entries: if gl_entries:
@ -209,7 +268,16 @@ def make_gl_entries(doc, credit_account, debit_account, against,
frappe.db.commit() frappe.db.commit()
except: except:
frappe.db.rollback() frappe.db.rollback()
title = _("Error while processing deferred accounting for {0}").format(doc.name)
traceback = frappe.get_traceback() traceback = frappe.get_traceback()
frappe.log_error(message=traceback , title=title) frappe.log_error(message=traceback)
sendmail_to_system_managers(title, traceback)
frappe.flags.deferred_accounting_error = True
def send_mail(deferred_process):
title = _("Error while processing deferred accounting for {0}".format(deferred_process))
content = _("""
Deferred accounting failed for some invoices:
Please check Process Deferred Accounting {0}
and submit manually after resolving errors
""").format(get_link_to_form('Process Deferred Accounting', deferred_process))
sendmail_to_system_managers(title, content)

View File

@ -8,7 +8,7 @@
{ {
"hidden": 0, "hidden": 0,
"label": "General Ledger", "label": "General Ledger",
"links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make journal entries from a template.\",\n \"label\": \"Journal Entry Template\",\n \"name\": \"Journal Entry Template\",\n \"type\": \"doctype\"\n },\n \n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]"
}, },
{ {
"hidden": 0, "hidden": 0,
@ -18,7 +18,7 @@
{ {
"hidden": 0, "hidden": 0,
"label": "Accounts Payable", "label": "Accounts Payable",
"links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Billed\",\n \"name\": \"Purchase Order Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
}, },
{ {
"hidden": 0, "hidden": 0,
@ -45,11 +45,6 @@
"label": "Bank Statement", "label": "Bank Statement",
"links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]" "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
}, },
{
"hidden": 0,
"label": "Banking and Payments",
"links": "[\n {\n \"description\": \"Match non-linked Invoices and Payments.\",\n \"label\": \"Match Payments with Invoices\",\n \"name\": \"Payment Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update bank payment dates with journals.\",\n \"label\": \"Update Bank Transaction Dates\",\n \"name\": \"Bank Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Invoice Discounting\",\n \"name\": \"Invoice Discounting\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Clearance Summary\",\n \"name\": \"Bank Clearance Summary\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Guarantee\",\n \"name\": \"Bank Guarantee\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup cheque dimensions for printing\",\n \"label\": \"Cheque Print Template\",\n \"name\": \"Cheque Print Template\",\n \"type\": \"doctype\"\n }\n]"
},
{ {
"hidden": 0, "hidden": 0,
"label": "Subscription Management", "label": "Subscription Management",
@ -89,8 +84,8 @@
"category": "Modules", "category": "Modules",
"charts": [ "charts": [
{ {
"chart_name": "Bank Balance", "chart_name": "Profit and Loss",
"label": "Bank Balance" "label": "Profit and Loss"
} }
], ],
"creation": "2020-03-02 15:41:59.515192", "creation": "2020-03-02 15:41:59.515192",
@ -99,23 +94,39 @@
"docstatus": 0, "docstatus": 0,
"doctype": "Desk Page", "doctype": "Desk Page",
"extends_another_page": 0, "extends_another_page": 0,
"icon": "", "hide_custom": 0,
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Accounting", "label": "Accounting",
"modified": "2020-04-01 11:28:50.925719", "modified": "2020-05-27 20:34:50.949772",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting", "name": "Accounting",
"onboarding": "Accounts",
"owner": "Administrator", "owner": "Administrator",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"shortcuts": [ "shortcuts": [
{ {
"label": "Account", "label": "Chart Of Accounts",
"link_to": "Account", "link_to": "Account",
"type": "DocType" "type": "DocType"
}, },
{
"label": "Sales Invoice",
"link_to": "Sales Invoice",
"type": "DocType"
},
{
"label": "Purchase Invoice",
"link_to": "Purchase Invoice",
"type": "DocType"
},
{
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
},
{ {
"label": "Journal Entry", "label": "Journal Entry",
"link_to": "Journal Entry", "link_to": "Journal Entry",
@ -136,11 +147,6 @@
"link_to": "General Ledger", "link_to": "General Ledger",
"type": "Report" "type": "Report"
}, },
{
"label": "Profit and Loss Statement",
"link_to": "Profit and Loss Statement",
"type": "Report"
},
{ {
"label": "Trial Balance", "label": "Trial Balance",
"link_to": "Trial Balance", "link_to": "Trial Balance",

View File

@ -162,9 +162,9 @@ def toggle_disabling(doc):
def get_doctypes_with_dimensions(): def get_doctypes_with_dimensions():
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
"Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item", "Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
"Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", "Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
"Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule", "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
"Subscription Plan"] "Subscription Plan"]
@ -206,12 +206,13 @@ def get_dimension_filters():
WHERE disabled = 0 WHERE disabled = 0
""", as_dict=1) """, as_dict=1)
default_dimensions = frappe.db.sql("""SELECT parent, company, default_dimension default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension
FROM `tabAccounting Dimension Detail`""", as_dict=1) FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
WHERE c.parent = p.name""", as_dict=1)
default_dimensions_map = {} default_dimensions_map = {}
for dimension in default_dimensions: for dimension in default_dimensions:
default_dimensions_map.setdefault(dimension['company'], {}) default_dimensions_map.setdefault(dimension.company, {})
default_dimensions_map[dimension['company']][dimension['parent']] = dimension['default_dimension'] default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension
return dimension_filters, default_dimensions_map return dimension_filters, default_dimensions_map

View File

@ -42,7 +42,7 @@ class AccountingPeriod(Document):
def get_doctypes_for_closing(self): def get_doctypes_for_closing(self):
docs_for_closing = [] docs_for_closing = []
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \ doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
"Bank Reconciliation", "Asset", "Stock Entry"] "Bank Clearance", "Asset", "Stock Entry"]
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes] closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
for closed_doctype in closed_doctypes: for closed_doctype in closed_doctypes:
docs_for_closing.append(closed_doctype) docs_for_closing.append(closed_doctype)

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2013-06-24 15:49:57", "creation": "2013-06-24 15:49:57",
"description": "Settings for Accounts", "description": "Settings for Accounts",
"doctype": "DocType", "doctype": "DocType",
@ -21,6 +22,7 @@
"allow_cost_center_in_entry_of_bs_account", "allow_cost_center_in_entry_of_bs_account",
"add_taxes_from_item_tax_template", "add_taxes_from_item_tax_template",
"automatically_fetch_payment_terms", "automatically_fetch_payment_terms",
"automatically_process_deferred_accounting_entry",
"print_settings", "print_settings",
"show_inclusive_tax_in_print", "show_inclusive_tax_in_print",
"column_break_12", "column_break_12",
@ -77,11 +79,13 @@
"options": "Role" "options": "Role"
}, },
{ {
"default": "0",
"fieldname": "check_supplier_invoice_uniqueness", "fieldname": "check_supplier_invoice_uniqueness",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Check Supplier Invoice Number Uniqueness" "label": "Check Supplier Invoice Number Uniqueness"
}, },
{ {
"default": "0",
"fieldname": "make_payment_via_journal_entry", "fieldname": "make_payment_via_journal_entry",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Make Payment via Journal Entry" "label": "Make Payment via Journal Entry"
@ -105,6 +109,7 @@
"label": "Book Asset Depreciation Entry Automatically" "label": "Book Asset Depreciation Entry Automatically"
}, },
{ {
"default": "0",
"fieldname": "allow_cost_center_in_entry_of_bs_account", "fieldname": "allow_cost_center_in_entry_of_bs_account",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Allow Cost Center In Entry of Balance Sheet Account" "label": "Allow Cost Center In Entry of Balance Sheet Account"
@ -121,6 +126,7 @@
"label": "Print Settings" "label": "Print Settings"
}, },
{ {
"default": "0",
"fieldname": "show_inclusive_tax_in_print", "fieldname": "show_inclusive_tax_in_print",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Show Inclusive Tax In Print" "label": "Show Inclusive Tax In Print"
@ -130,6 +136,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"default": "0",
"fieldname": "show_payment_schedule_in_print", "fieldname": "show_payment_schedule_in_print",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Show Payment Schedule in Print" "label": "Show Payment Schedule in Print"
@ -166,6 +173,7 @@
"label": "Use Custom Cash Flow Format" "label": "Use Custom Cash Flow Format"
}, },
{ {
"default": "0",
"fieldname": "automatically_fetch_payment_terms", "fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Automatically Fetch Payment Terms" "label": "Automatically Fetch Payment Terms"
@ -175,12 +183,19 @@
"fieldname": "over_billing_allowance", "fieldname": "over_billing_allowance",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Over Billing Allowance (%)" "label": "Over Billing Allowance (%)"
},
{
"default": "1",
"fieldname": "automatically_process_deferred_accounting_entry",
"fieldtype": "Check",
"label": "Automatically Process Deferred Accounting Entry"
} }
], ],
"icon": "icon-cog", "icon": "icon-cog",
"idx": 1, "idx": 1,
"issingle": 1, "issingle": 1,
"modified": "2019-07-04 18:20:55.789946", "links": [],
"modified": "2019-12-19 16:58:17.395595",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",
@ -205,6 +220,7 @@
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "modified",
"sort_order": "ASC", "sort_order": "ASC",
"track_changes": 1 "track_changes": 1
} }

View File

@ -1,74 +1,32 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-04-16 21:50:05.860195", "creation": "2018-04-16 21:50:05.860195",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [
"company"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Company", "label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company", "options": "Company",
"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,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 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": "2018-04-20 14:00:46.014502", "modified": "2020-05-01 12:32:34.044911",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Allowed To Transact With", "name": "Allowed To Transact With",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,
"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, "track_changes": 1
"track_seen": 0
} }

View File

@ -1,7 +1,7 @@
// 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("Bank Reconciliation", { 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");
}, },

View File

@ -0,0 +1,130 @@
{
"allow_copy": 1,
"creation": "2013-01-10 16:34:05",
"doctype": "DocType",
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"account",
"account_currency",
"from_date",
"to_date",
"column_break_5",
"bank_account",
"include_reconciled_entries",
"include_pos_transactions",
"get_payment_entries",
"section_break_10",
"payment_entries",
"update_clearance_date",
"total_amount"
],
"fields": [
{
"fetch_from": "bank_account.account",
"fetch_if_empty": 1,
"fieldname": "account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Account",
"options": "Account",
"reqd": 1
},
{
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 1,
"label": "Account Currency",
"options": "Currency",
"print_hide": 1
},
{
"fieldname": "from_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "From Date",
"reqd": 1
},
{
"fieldname": "to_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "To Date",
"reqd": 1
},
{
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"description": "Select the Bank Account to reconcile.",
"fieldname": "bank_account",
"fieldtype": "Link",
"label": "Bank Account",
"options": "Bank Account"
},
{
"default": "0",
"fieldname": "include_reconciled_entries",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Include Reconciled Entries"
},
{
"default": "0",
"fieldname": "include_pos_transactions",
"fieldtype": "Check",
"label": "Include POS Transactions"
},
{
"fieldname": "get_payment_entries",
"fieldtype": "Button",
"label": "Get Payment Entries"
},
{
"fieldname": "section_break_10",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 1,
"fieldname": "payment_entries",
"fieldtype": "Table",
"label": "Payment Entries",
"options": "Bank Clearance Detail"
},
{
"fieldname": "update_clearance_date",
"fieldtype": "Button",
"label": "Update Clearance Date"
},
{
"fieldname": "total_amount",
"fieldtype": "Currency",
"label": "Total Amount",
"options": "account_currency",
"read_only": 1
}
],
"hide_toolbar": 1,
"icon": "fa fa-check",
"idx": 1,
"issingle": 1,
"modified": "2020-04-06 16:12:06.628008",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Clearance",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"read": 1,
"role": "Accounts User",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 1,
"sort_field": "modified",
"sort_order": "ASC"
}

View File

@ -11,7 +11,7 @@ form_grid_templates = {
"journal_entries": "templates/form_grid/bank_reconciliation_grid.html" "journal_entries": "templates/form_grid/bank_reconciliation_grid.html"
} }
class BankReconciliation(Document): class BankClearance(Document):
def get_payment_entries(self): def get_payment_entries(self):
if not (self.from_date and self.to_date): if not (self.from_date and self.to_date):
frappe.throw(_("From Date and To Date are Mandatory")) frappe.throw(_("From Date and To Date are Mandatory"))

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestBankClearance(unittest.TestCase):
pass

View File

@ -0,0 +1 @@
Detail of transaction for parent Bank Clearance.

View File

@ -326,7 +326,7 @@
"modified": "2019-01-07 16:52:07.174687", "modified": "2019-01-07 16:52:07.174687",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Reconciliation Detail", "name": "Bank Clearance Detail",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,

View File

@ -5,5 +5,5 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
class BankReconciliationDetail(Document): class BankClearanceDetail(Document):
pass pass

View File

@ -1,484 +0,0 @@
{
"allow_copy": 1,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-01-10 16:34:05",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "bank_account.account",
"fetch_if_empty": 1,
"fieldname": "account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Account Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"print_hide": 1,
"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,
"fetch_if_empty": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "From Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "To Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"description": "Select the Bank Account to reconcile.",
"fetch_if_empty": 0,
"fieldname": "bank_account",
"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": "Bank Account",
"length": 0,
"no_copy": 0,
"options": "Bank Account",
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "include_reconciled_entries",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Include Reconciled Entries",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "include_pos_transactions",
"fieldtype": "Check",
"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": "Include POS Transactions",
"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,
"fetch_if_empty": 0,
"fieldname": "get_payment_entries",
"fieldtype": "Button",
"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": "Get Payment Entries",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 1,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payment_entries",
"fieldtype": "Table",
"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": "Payment Entries",
"length": 0,
"no_copy": 0,
"options": "Bank Reconciliation Detail",
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "update_clearance_date",
"fieldtype": "Button",
"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": "Update Clearance Date",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"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,
"fetch_if_empty": 0,
"fieldname": "total_amount",
"fieldtype": "Currency",
"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": "Total Amount",
"length": 0,
"no_copy": 0,
"options": "account_currency",
"permlevel": 0,
"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
}
],
"has_web_view": 0,
"hide_toolbar": 1,
"icon": "fa fa-check",
"idx": 1,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2020-01-22 00:00:00.000000",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 1,
"show_name_in_global_search": 0,
"sort_order": "ASC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,22 +0,0 @@
QUnit.module('Account');
QUnit.test("test Bank Reconciliation", function(assert) {
assert.expect(0);
let done = assert.async();
frappe.run_serially([
() => frappe.set_route('Form', 'Bank Reconciliation'),
() => cur_frm.set_value('bank_account','Cash - FT'),
() => frappe.click_button('Get Payment Entries'),
() => {
for(var i=0;i<=cur_frm.doc.payment_entries.length-1;i++){
cur_frm.doc.payment_entries[i].clearance_date = frappe.datetime.add_days(frappe.datetime.now_date(), 2);
}
},
() => {cur_frm.refresh_fields('payment_entries');},
() => frappe.click_button('Update Clearance Date'),
() => frappe.timeout(0.5),
() => frappe.click_button('Close'),
() => done()
]);
});

View File

@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestBankReconciliation(unittest.TestCase):
pass

View File

@ -1 +0,0 @@
Detail of transaction for parent Bank Reconciliation.

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from frappe.utils import nowdate from frappe.utils import nowdate, now_datetime
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
@ -13,27 +13,28 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestBudget(unittest.TestCase): class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self): def test_monthly_budget_crossed_ignore(self):
set_total_expense_zero("2013-02-28", "cost_center") set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True) "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name})) {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
budget.cancel() budget.cancel()
jv.cancel()
def test_monthly_budget_crossed_stop1(self): def test_monthly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "cost_center") set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28") "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@ -41,14 +42,14 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_exception_approver_role(self): def test_exception_approver_role(self):
set_total_expense_zero("2013-02-28", "cost_center") set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-03-02") "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@ -112,16 +113,17 @@ class TestBudget(unittest.TestCase):
budget.load_from_db() budget.load_from_db()
budget.cancel() budget.cancel()
po.cancel()
def test_monthly_budget_crossed_stop2(self): def test_monthly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "project") set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-02-28") "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@ -129,86 +131,76 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
def test_yearly_budget_crossed_stop1(self): def test_yearly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "cost_center") set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC", posting_date="2013-03-28") "_Test Bank - _TC", 250000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
budget.cancel() budget.cancel()
def test_yearly_budget_crossed_stop2(self): def test_yearly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "project") set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-03-28") "_Test Bank - _TC", 250000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
budget.cancel() budget.cancel()
def test_monthly_budget_on_cancellation1(self): def test_monthly_budget_on_cancellation1(self):
set_total_expense_zero("2013-02-28", "cost_center") set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", for i in range(now_datetime().month):
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True) jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv1.name})) {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
self.assertRaises(BudgetError, jv1.cancel) self.assertRaises(BudgetError, jv.cancel)
budget.load_from_db() budget.load_from_db()
budget.cancel() budget.cancel()
def test_monthly_budget_on_cancellation2(self): def test_monthly_budget_on_cancellation2(self):
set_total_expense_zero("2013-02-28", "project") set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", for i in range(now_datetime().month):
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project") jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project")
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv1.name})) {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project")
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
self.assertRaises(BudgetError, jv1.cancel) self.assertRaises(BudgetError, jv.cancel)
budget.load_from_db() budget.load_from_db()
budget.cancel() budget.cancel()
def test_monthly_budget_against_group_cost_center(self): def test_monthly_budget_against_group_cost_center(self):
set_total_expense_zero("2013-02-28", "cost_center") set_total_expense_zero(nowdate(), "cost_center")
set_total_expense_zero("2013-02-28", "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("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date="2013-02-28") "_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@ -231,7 +223,7 @@ class TestBudget(unittest.TestCase):
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, cost_center, posting_date="2013-02-28") "_Test Bank - _TC", 40000, cost_center, posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@ -246,12 +238,14 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
else: else:
budget_against = budget_against_CC or "_Test Cost Center - _TC" budget_against = budget_against_CC or "_Test Cost Center - _TC"
fiscal_year = get_fiscal_year(nowdate())[0]
args = frappe._dict({ args = frappe._dict({
"account": "_Test Account Cost for Goods Sold - _TC", "account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"monthly_end_date": posting_date, "monthly_end_date": posting_date,
"company": "_Test Company", "company": "_Test Company",
"fiscal_year": "_Test Fiscal Year 2013", "fiscal_year": fiscal_year,
"budget_against_field": budget_against_field, "budget_against_field": budget_against_field,
}) })
@ -263,10 +257,10 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
if existing_expense: if existing_expense:
if budget_against_field == "cost_center": if budget_against_field == "cost_center":
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True) "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
elif budget_against_field == "project": elif budget_against_field == "project":
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28") "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date=nowdate())
def make_budget(**args): def make_budget(**args):
args = frappe._dict(args) args = frappe._dict(args)
@ -274,10 +268,13 @@ def make_budget(**args):
budget_against=args.budget_against budget_against=args.budget_against
cost_center=args.cost_center cost_center=args.cost_center
fiscal_year = get_fiscal_year(nowdate())[0]
if budget_against == "Project": if budget_against == "Project":
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", "_Test Project/_Test Fiscal Year 2013%")}) project_name = "{0}%".format("_Test Project/" + fiscal_year)
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/_Test Fiscal Year 2013") cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)}) budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)})
for d in budget_list: 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)
@ -290,8 +287,10 @@ def make_budget(**args):
else: else:
budget.cost_center =cost_center or "_Test Cost Center - _TC" budget.cost_center =cost_center or "_Test Cost Center - _TC"
monthly_distribution = frappe.get_doc("Monthly Distribution", "_Test Distribution")
monthly_distribution.fiscal_year = fiscal_year
budget.fiscal_year = "_Test Fiscal Year 2013" budget.fiscal_year = fiscal_year
budget.monthly_distribution = "_Test Distribution" budget.monthly_distribution = "_Test Distribution"
budget.company = "_Test Company" budget.company = "_Test Company"
budget.applicable_on_booking_actual_expenses = 1 budget.applicable_on_booking_actual_expenses = 1
@ -300,7 +299,7 @@ def make_budget(**args):
budget.budget_against = budget_against budget.budget_against = budget_against
budget.append("accounts", { budget.append("accounts", {
"account": "_Test Account Cost for Goods Sold - _TC", "account": "_Test Account Cost for Goods Sold - _TC",
"budget_amount": 100000 "budget_amount": 200000
}) })
if args.applicable_on_material_request: if args.applicable_on_material_request:

View File

@ -18,7 +18,7 @@ frappe.ui.form.on('Cost Center', {
}, },
refresh: function(frm) { refresh: function(frm) {
if (!frm.is_new()) { if (!frm.is_new()) {
frm.add_custom_button(__('Update Cost Center Number'), function () { frm.add_custom_button(__('Update Cost Center Name / Number'), function () {
frm.trigger("update_cost_center_number"); frm.trigger("update_cost_center_number");
}); });
} }
@ -47,35 +47,45 @@ frappe.ui.form.on('Cost Center', {
}, },
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 Number'), title: __('Update Cost Center Name / Number'),
fields: [ fields: [
{ {
"label": 'Cost Center Number', "label": "Cost Center Name",
"fieldname": "cost_center_name",
"fieldtype": "Data",
"reqd": 1,
"default": frm.doc.cost_center_name
},
{
"label": "Cost Center Number",
"fieldname": "cost_center_number", "fieldname": "cost_center_number",
"fieldtype": "Data", "fieldtype": "Data",
"reqd": 1 "reqd": 1,
"default": frm.doc.cost_center_number
} }
], ],
primary_action: function() { primary_action: function() {
var data = d.get_values(); var data = d.get_values();
if(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;
} }
frappe.dom.freeze();
frappe.call({ frappe.call({
method: "erpnext.accounts.utils.update_number_field", method: "erpnext.accounts.utils.update_cost_center",
args: { args: {
doctype_name: frm.doc.doctype, docname: frm.doc.name,
name: frm.doc.name, cost_center_name: data.cost_center_name,
field_name: d.fields[0].fieldname, cost_center_number: data.cost_center_number,
number_value: data.cost_center_number,
company: frm.doc.company company: frm.doc.company
}, },
callback: function(r) { callback: function(r) {
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_number", data.cost_center_number); me.frm.set_value("cost_center_number", data.cost_center_number);
} }
d.hide(); d.hide();

View File

@ -2,7 +2,6 @@
"actions": [], "actions": [],
"allow_copy": 1, "allow_copy": 1,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1,
"creation": "2013-01-23 19:57:17", "creation": "2013-01-23 19:57:17",
"description": "Track separate Income and Expense for product verticals or divisions.", "description": "Track separate Income and Expense for product verticals or divisions.",
"doctype": "DocType", "doctype": "DocType",
@ -126,7 +125,7 @@
"idx": 1, "idx": 1,
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2020-03-18 17:59:04.321637", "modified": "2020-04-29 16:09:30.025214",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Cost Center", "name": "Cost Center",

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "ACC-GLE-.YYYY.-.#####", "autoname": "ACC-GLE-.YYYY.-.#####",
"creation": "2013-01-10 16:34:06", "creation": "2013-01-10 16:34:06",
"doctype": "DocType", "doctype": "DocType",
@ -30,7 +31,8 @@
"company", "company",
"finance_book", "finance_book",
"to_rename", "to_rename",
"due_date" "due_date",
"is_cancelled"
], ],
"fields": [ "fields": [
{ {
@ -245,12 +247,18 @@
"fieldname": "due_date", "fieldname": "due_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Due Date" "label": "Due Date"
},
{
"default": "0",
"fieldname": "is_cancelled",
"fieldtype": "Check",
"label": "Is Cancelled"
} }
], ],
"icon": "fa fa-list", "icon": "fa fa-list",
"idx": 1, "idx": 1,
"in_create": 1, "in_create": 1,
"modified": "2020-03-28 16:22:33.766994", "modified": "2020-04-07 16:22:33.766994",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "GL Entry", "name": "GL Entry",

View File

@ -30,23 +30,20 @@ class GLEntry(Document):
self.pl_must_have_cost_center() self.pl_must_have_cost_center()
self.validate_cost_center() self.validate_cost_center()
if not self.flags.from_repost:
self.check_pl_account() self.check_pl_account()
self.validate_party() self.validate_party()
self.validate_currency() self.validate_currency()
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False): def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
if not from_repost:
self.validate_account_details(adv_adj) self.validate_account_details(adv_adj)
self.validate_dimensions_for_pl_and_bs() self.validate_dimensions_for_pl_and_bs()
check_freezing_date(self.posting_date, adv_adj)
validate_frozen_account(self.account, adv_adj) validate_frozen_account(self.account, adv_adj)
validate_balance_type(self.account, adv_adj) validate_balance_type(self.account, adv_adj)
# Update outstanding amt on against voucher # Update outstanding amt on against voucher
if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \ if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
and self.against_voucher and update_outstanding == 'Yes' and not from_repost: and self.against_voucher and update_outstanding == 'Yes':
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type, update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
self.against_voucher) self.against_voucher)
@ -115,8 +112,8 @@ class GLEntry(Document):
from tabAccount where name=%s""", self.account, as_dict=1)[0] from tabAccount where name=%s""", self.account, as_dict=1)[0]
if ret.is_group==1: if ret.is_group==1:
frappe.throw(_("{0} {1}: Account {2} cannot be a Group") frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in
.format(self.voucher_type, self.voucher_no, self.account)) transactions''').format(self.voucher_type, self.voucher_no, self.account))
if ret.docstatus==2: if ret.docstatus==2:
frappe.throw(_("{0} {1}: Account {2} is inactive") frappe.throw(_("{0} {1}: Account {2} is inactive")
@ -159,7 +156,6 @@ class GLEntry(Document):
if self.party_type and self.party: if self.party_type and self.party:
validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency) validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
def validate_and_set_fiscal_year(self): def validate_and_set_fiscal_year(self):
if not self.fiscal_year: if not self.fiscal_year:
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0] self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
@ -176,19 +172,6 @@ def validate_balance_type(account, adv_adj=False):
(balance_must_be=="Credit" and flt(balance) > 0): (balance_must_be=="Credit" and flt(balance) > 0):
frappe.throw(_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be))) frappe.throw(_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be)))
def check_freezing_date(posting_date, adv_adj=False):
"""
Nobody can do GL Entries where posting date is before freezing date
except authorized person
"""
if not adv_adj:
acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
if acc_frozen_upto:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if getdate(posting_date) <= getdate(acc_frozen_upto) \
and not frozen_accounts_modifier in frappe.get_roles():
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False): def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
if party_type and party: if party_type and party:
party_condition = " and party_type={0} and party={1}"\ party_condition = " and party_type={0} and party={1}"\

View File

@ -8,6 +8,7 @@ from frappe import _
from frappe.utils import flt, getdate, nowdate, add_days from frappe.utils import flt, getdate, nowdate, add_days
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
class InvoiceDiscounting(AccountsController): class InvoiceDiscounting(AccountsController):
def validate(self): def validate(self):
@ -81,10 +82,15 @@ class InvoiceDiscounting(AccountsController):
def make_gl_entries(self): def make_gl_entries(self):
company_currency = frappe.get_cached_value('Company', self.company, "default_currency") company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
gl_entries = [] gl_entries = []
invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
accounting_dimensions = get_accounting_dimensions()
invoice_fields.extend(accounting_dimensions)
for d in self.invoices: for d in self.invoices:
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
if d.outstanding_amount: if d.outstanding_amount:
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate, outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
@ -102,7 +108,7 @@ class InvoiceDiscounting(AccountsController):
"cost_center": inv.cost_center, "cost_center": inv.cost_center,
"against_voucher": d.sales_invoice, "against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice" "against_voucher_type": "Sales Invoice"
}, inv.party_account_currency)) }, inv.party_account_currency, item=inv))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": self.accounts_receivable_credit, "account": self.accounts_receivable_credit,
@ -115,7 +121,7 @@ class InvoiceDiscounting(AccountsController):
"cost_center": inv.cost_center, "cost_center": inv.cost_center,
"against_voucher": d.sales_invoice, "against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice" "against_voucher_type": "Sales Invoice"
}, ar_credit_account_currency)) }, ar_credit_account_currency, item=inv))
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No') make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')

View File

@ -12,7 +12,6 @@ frappe.ui.form.on("Journal Entry", {
refresh: function(frm) { refresh: function(frm) {
erpnext.toggle_naming_series(); erpnext.toggle_naming_series();
frm.cscript.voucher_type(frm.doc);
if(frm.doc.docstatus==1) { if(frm.doc.docstatus==1) {
frm.add_custom_button(__('Ledger'), function() { frm.add_custom_button(__('Ledger'), function() {
@ -120,9 +119,78 @@ frappe.ui.form.on("Journal Entry", {
} }
} }
}); });
},
voucher_type: function(frm){
if(!frm.doc.company) return null;
if((!(frm.doc.accounts || []).length) || ((frm.doc.accounts || []).length === 1 && !frm.doc.accounts[0].account)) {
if(in_list(["Bank Entry", "Cash Entry"], frm.doc.voucher_type)) {
return frappe.call({
type: "GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
"account_type": (frm.doc.voucher_type=="Bank Entry" ?
"Bank" : (frm.doc.voucher_type=="Cash Entry" ? "Cash" : null)),
"company": frm.doc.company
},
callback: function(r) {
if(r.message) {
// If default company bank account not set
if(!$.isEmptyObject(r.message)){
update_jv_details(frm.doc, [r.message]);
}
}
}
});
}
else if(frm.doc.voucher_type=="Opening Entry") {
return frappe.call({
type:"GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
args: {
"company": frm.doc.company
},
callback: function(r) {
frappe.model.clear_table(frm.doc, "accounts");
if(r.message) {
update_jv_details(frm.doc, r.message);
}
cur_frm.set_value("is_opening", "Yes");
}
});
}
}
},
from_template: function(frm){
if (frm.doc.from_template){
frappe.db.get_doc("Journal Entry Template", frm.doc.from_template)
.then((doc) => {
frappe.model.clear_table(frm.doc, "accounts");
frm.set_value({
"company": doc.company,
"voucher_type": doc.voucher_type,
"naming_series": doc.naming_series,
"is_opening": doc.is_opening,
"multi_currency": doc.multi_currency
})
update_jv_details(frm.doc, doc.accounts);
});
}
} }
}); });
var update_jv_details = function(doc, r) {
$.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
row.account = d.account;
row.balance = d.balance;
});
refresh_field("accounts");
}
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
onload: function() { onload: function() {
this.load_defaults(); this.load_defaults();
@ -375,56 +443,6 @@ cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
cur_frm.pformat.print_heading = __("Journal Entry"); cur_frm.pformat.print_heading = __("Journal Entry");
} }
cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
cur_frm.set_df_property("cheque_no", "reqd", doc.voucher_type=="Bank Entry");
cur_frm.set_df_property("cheque_date", "reqd", doc.voucher_type=="Bank Entry");
if(!doc.company) return;
var update_jv_details = function(doc, r) {
$.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
row.account = d.account;
row.balance = d.balance;
});
refresh_field("accounts");
}
if((!(doc.accounts || []).length) || ((doc.accounts || []).length==1 && !doc.accounts[0].account)) {
if(in_list(["Bank Entry", "Cash Entry"], doc.voucher_type)) {
return frappe.call({
type: "GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
"account_type": (doc.voucher_type=="Bank Entry" ?
"Bank" : (doc.voucher_type=="Cash Entry" ? "Cash" : null)),
"company": doc.company
},
callback: function(r) {
if(r.message) {
update_jv_details(doc, [r.message]);
}
}
})
} else if(doc.voucher_type=="Opening Entry") {
return frappe.call({
type:"GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
args: {
"company": doc.company
},
callback: function(r) {
frappe.model.clear_table(doc, "accounts");
if(r.message) {
update_jv_details(doc, r.message);
}
cur_frm.set_value("is_opening", "Yes")
}
})
}
}
}
frappe.ui.form.on("Journal Entry Account", { frappe.ui.form.on("Journal Entry Account", {
party: function(frm, cdt, cdn) { party: function(frm, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn); var d = frappe.get_doc(cdt, cdn);

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2013-03-25 10:53:52", "creation": "2013-03-25 10:53:52",
@ -10,10 +11,11 @@
"title", "title",
"voucher_type", "voucher_type",
"naming_series", "naming_series",
"column_break1",
"posting_date",
"company",
"finance_book", "finance_book",
"column_break1",
"from_template",
"company",
"posting_date",
"2_add_edit_gl_entries", "2_add_edit_gl_entries",
"accounts", "accounts",
"section_break99", "section_break99",
@ -157,6 +159,7 @@
"in_global_search": 1, "in_global_search": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Reference Number", "label": "Reference Number",
"mandatory_depends_on": "eval:doc.voucher_type == \"Bank Entry\"",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "cheque_no", "oldfieldname": "cheque_no",
"oldfieldtype": "Data", "oldfieldtype": "Data",
@ -166,6 +169,7 @@
"fieldname": "cheque_date", "fieldname": "cheque_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Reference Date", "label": "Reference Date",
"mandatory_depends_on": "eval:doc.voucher_type == \"Bank Entry\"",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "cheque_date", "oldfieldname": "cheque_date",
"oldfieldtype": "Date", "oldfieldtype": "Date",
@ -484,12 +488,22 @@
"options": "Journal Entry", "options": "Journal Entry",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
},
{
"fieldname": "from_template",
"fieldtype": "Link",
"label": "From Template",
"no_copy": 1,
"options": "Journal Entry Template",
"print_hide": 1,
"report_hide": 1
} }
], ],
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"idx": 176, "idx": 176,
"is_submittable": 1, "is_submittable": 1,
"modified": "2020-01-16 13:05:30.634226", "links": [],
"modified": "2020-04-29 10:55:28.240916",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@ -57,6 +57,7 @@ class JournalEntry(AccountsController):
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self) unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name) unlink_ref_doc_from_salary_slip(self.name)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.make_gl_entries(1) self.make_gl_entries(1)
self.update_advance_paid() self.update_advance_paid()
self.update_expense_claim() self.update_expense_claim()
@ -559,20 +560,20 @@ class JournalEntry(AccountsController):
if self.write_off_based_on == 'Accounts Receivable': if self.write_off_based_on == 'Accounts Receivable':
jd1.party_type = "Customer" jd1.party_type = "Customer"
jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts")) jd1.credit_in_account_currency = flt(d.outstanding_amount, self.precision("credit", "accounts"))
jd1.reference_type = "Sales Invoice" jd1.reference_type = "Sales Invoice"
jd1.reference_name = cstr(d.name) jd1.reference_name = cstr(d.name)
elif self.write_off_based_on == 'Accounts Payable': elif self.write_off_based_on == 'Accounts Payable':
jd1.party_type = "Supplier" jd1.party_type = "Supplier"
jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts")) jd1.debit_in_account_currency = flt(d.outstanding_amount, self.precision("debit", "accounts"))
jd1.reference_type = "Purchase Invoice" jd1.reference_type = "Purchase Invoice"
jd1.reference_name = cstr(d.name) jd1.reference_name = cstr(d.name)
jd2 = self.append('accounts', {}) jd2 = self.append('accounts', {})
if self.write_off_based_on == 'Accounts Receivable': if self.write_off_based_on == 'Accounts Receivable':
jd2.debit = total jd2.debit_in_account_currency = total
elif self.write_off_based_on == 'Accounts Payable': elif self.write_off_based_on == 'Accounts Payable':
jd2.credit = total jd2.credit_in_account_currency = total
self.validate_total_debit_and_credit() self.validate_total_debit_and_credit()
@ -594,7 +595,7 @@ class JournalEntry(AccountsController):
for d in self.accounts: for d in self.accounts:
if d.reference_type=="Expense Claim" and d.reference_name: if d.reference_type=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name) doc = frappe.get_doc("Expense Claim", d.reference_name)
update_reimbursed_amount(doc) update_reimbursed_amount(doc, jv=self.name)
def validate_expense_claim(self): def validate_expense_claim(self):

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "hash", "autoname": "hash",
"creation": "2013-02-22 01:27:39", "creation": "2013-02-22 01:27:39",
"doctype": "DocType", "doctype": "DocType",
@ -271,7 +272,8 @@
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2020-01-13 12:41:33.968025", "links": [],
"modified": "2020-04-25 01:47:49.060128",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",

View File

@ -0,0 +1,91 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Journal Entry Template", {
setup: function(frm) {
frappe.model.set_default_values(frm.doc);
frm.set_query("account" ,"accounts", function(){
var filters = {
company: frm.doc.company,
is_group: 0
};
if(!frm.doc.multi_currency) {
$.extend(filters, {
account_currency: frappe.get_doc(":Company", frm.doc.company).default_currency
});
}
return { filters: filters };
});
frappe.call({
type: "GET",
method: "erpnext.accounts.doctype.journal_entry_template.journal_entry_template.get_naming_series",
callback: function(r){
if(r.message){
frm.set_df_property("naming_series", "options", r.message.split("\n"));
frm.set_value("naming_series", r.message.split("\n")[0]);
frm.refresh_field("naming_series");
}
}
});
},
voucher_type: function(frm) {
var add_accounts = function(doc, r) {
$.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Template Account", "accounts");
row.account = d.account;
});
refresh_field("accounts");
};
if(!frm.doc.company) return;
frm.trigger("clear_child");
switch(frm.doc.voucher_type){
case "Opening Entry":
frm.set_value("is_opening", "Yes");
frappe.call({
type:"GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
args: {
"company": frm.doc.company
},
callback: function(r) {
if(r.message) {
add_accounts(frm.doc, r.message);
}
}
});
break;
case "Bank Entry":
case "Cash Entry":
frappe.call({
type: "GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
"account_type": (frm.doc.voucher_type=="Bank Entry" ?
"Bank" : (frm.doc.voucher_type=="Cash Entry" ? "Cash" : null)),
"company": frm.doc.company
},
callback: function(r) {
if(r.message) {
// If default company bank account not set
if(!$.isEmptyObject(r.message)){
add_accounts(frm.doc, [r.message]);
}
}
}
});
break;
default:
frm.trigger("clear_child");
}
},
clear_child: function(frm){
frappe.model.clear_table(frm.doc, "accounts");
frm.refresh_field("accounts");
}
});

View File

@ -0,0 +1,134 @@
{
"actions": [],
"autoname": "field:template_title",
"creation": "2020-04-09 01:32:51.332301",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"section_break_1",
"template_title",
"voucher_type",
"naming_series",
"column_break_3",
"company",
"is_opening",
"multi_currency",
"section_break_3",
"accounts"
],
"fields": [
{
"fieldname": "section_break_1",
"fieldtype": "Section Break"
},
{
"fieldname": "voucher_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Journal Entry Type",
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation",
"reqd": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Company",
"options": "Company",
"remember_last_selected_value": 1,
"reqd": 1
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"default": "No",
"fieldname": "is_opening",
"fieldtype": "Select",
"label": "Is Opening",
"options": "No\nYes"
},
{
"fieldname": "accounts",
"fieldtype": "Table",
"label": "Accounting Entries",
"options": "Journal Entry Template Account"
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"print_hide": 1,
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "template_title",
"fieldtype": "Data",
"label": "Template Title",
"reqd": 1,
"unique": 1
},
{
"fieldname": "section_break_3",
"fieldtype": "Section Break"
},
{
"default": "0",
"fieldname": "multi_currency",
"fieldtype": "Check",
"label": "Multi Currency"
}
],
"links": [],
"modified": "2020-05-01 18:32:01.420488",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Template",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Auditor",
"share": 1
}
],
"search_fields": "voucher_type, company",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "template_title",
"track_changes": 1
}

View File

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class JournalEntryTemplate(Document):
pass
@frappe.whitelist()
def get_naming_series():
return frappe.get_meta("Journal Entry").get_field("naming_series").options

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestJournalEntryTemplate(unittest.TestCase):
pass

View File

@ -0,0 +1,31 @@
{
"actions": [],
"creation": "2020-04-09 01:48:42.783620",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account"
],
"fields": [
{
"fieldname": "account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Account",
"options": "Account",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-04-25 01:15:44.879839",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Template Account",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -1,13 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2018, 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
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe # import frappe
from frappe.model.document import Document from frappe.model.document import Document
class Video(Document): class JournalEntryTemplateAccount(Document):
def get_video(self):
pass pass

View File

@ -11,21 +11,9 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
}; };
}); });
frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) { if (frm.doc.company) {
return { frm.trigger('setup_company_filters');
filters: {
'company': doc.company
} }
};
});
frm.set_query('cost_center', function(doc) {
return {
filters: {
'company': doc.company
}
};
});
}, },
refresh: function(frm) { refresh: function(frm) {
@ -51,7 +39,37 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
}); });
}, },
setup_company_filters: function(frm) {
frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) {
return {
filters: {
'company': doc.company
}
};
});
frm.set_query('cost_center', function(doc) {
return {
filters: {
'company': doc.company
}
};
});
frm.set_query('temporary_opening_account', 'invoices', function(doc, cdt, cdn) {
return {
filters: {
'company': doc.company
}
}
});
},
company: function(frm) { company: function(frm) {
if (frm.doc.company) {
frm.trigger('setup_company_filters');
frappe.call({ frappe.call({
method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account', method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account',
args: { args: {
@ -64,6 +82,7 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
} }
} }
}) })
}
}, },
invoice_type: function(frm) { invoice_type: function(frm) {

View File

@ -60,6 +60,7 @@ class PaymentEntry(AccountsController):
self.set_remarks() self.set_remarks()
self.validate_duplicate_entry() self.validate_duplicate_entry()
self.validate_allocated_amount() self.validate_allocated_amount()
self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked() self.ensure_supplier_is_not_blocked()
self.set_status() self.set_status()
@ -75,6 +76,7 @@ class PaymentEntry(AccountsController):
self.set_status() self.set_status()
def on_cancel(self): def on_cancel(self):
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.setup_party_account_field() self.setup_party_account_field()
self.make_gl_entries(cancel=1) self.make_gl_entries(cancel=1)
self.update_outstanding_amounts() self.update_outstanding_amounts()
@ -226,6 +228,8 @@ class PaymentEntry(AccountsController):
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
elif self.party_type == "Employee": elif self.party_type == "Employee":
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance") valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
elif self.party_type == "Shareholder":
valid_reference_doctypes = ("Journal Entry")
for d in self.get("references"): for d in self.get("references"):
if not d.allocated_amount: if not d.allocated_amount:
@ -265,6 +269,25 @@ class PaymentEntry(AccountsController):
frappe.throw(_("{0} {1} must be submitted") frappe.throw(_("{0} {1} must be submitted")
.format(d.reference_doctype, d.reference_name)) .format(d.reference_doctype, d.reference_name))
def validate_paid_invoices(self):
no_oustanding_refs = {}
for d in self.get("references"):
if not d.allocated_amount:
continue
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
if outstanding_amount <= 0 and not is_return:
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
for k, v in no_oustanding_refs.items():
frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
If this is undesirable please cancel the corresponding Payment Entry.")
.format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")),
title=_("Warning"), indicator="orange")
def validate_journal_entry(self): def validate_journal_entry(self):
for d in self.get("references"): for d in self.get("references"):
if d.allocated_amount and d.reference_doctype == "Journal Entry": if d.allocated_amount and d.reference_doctype == "Journal Entry":
@ -428,8 +451,6 @@ class PaymentEntry(AccountsController):
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
def set_remarks(self): def set_remarks(self):
if self.remarks: return
if self.payment_type=="Internal Transfer": if self.payment_type=="Internal Transfer":
remarks = [_("Amount {0} {1} transferred from {2} to {3}") remarks = [_("Amount {0} {1} transferred from {2} to {3}")
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
@ -483,7 +504,7 @@ class PaymentEntry(AccountsController):
"against": against_account, "against": against_account,
"account_currency": self.party_account_currency, "account_currency": self.party_account_currency,
"cost_center": self.cost_center "cost_center": self.cost_center
}) }, item=self)
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit" dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
@ -527,7 +548,7 @@ class PaymentEntry(AccountsController):
"credit_in_account_currency": self.paid_amount, "credit_in_account_currency": self.paid_amount,
"credit": self.base_paid_amount, "credit": self.base_paid_amount,
"cost_center": self.cost_center "cost_center": self.cost_center
}) }, item=self)
) )
if self.payment_type in ("Receive", "Internal Transfer"): if self.payment_type in ("Receive", "Internal Transfer"):
gl_entries.append( gl_entries.append(
@ -538,7 +559,7 @@ class PaymentEntry(AccountsController):
"debit_in_account_currency": self.received_amount, "debit_in_account_currency": self.received_amount,
"debit": self.base_received_amount, "debit": self.base_received_amount,
"cost_center": self.cost_center "cost_center": self.cost_center
}) }, item=self)
) )
def add_deductions_gl_entries(self, gl_entries): def add_deductions_gl_entries(self, gl_entries):
@ -571,7 +592,7 @@ class PaymentEntry(AccountsController):
for d in self.get("references"): for d in self.get("references"):
if d.reference_doctype=="Expense Claim" and d.reference_name: if d.reference_doctype=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name) doc = frappe.get_doc("Expense Claim", d.reference_name)
update_reimbursed_amount(doc) update_reimbursed_amount(doc, self.name)
def on_recurring(self, reference_doc, auto_repeat_doc): def on_recurring(self, reference_doc, auto_repeat_doc):
self.reference_no = reference_doc.name self.reference_no = reference_doc.name

View File

@ -0,0 +1,12 @@
frappe.listview_settings['Payment Entry'] = {
onload: function(listview) {
listview.page.fields_dict.party_type.get_query = function() {
return {
"filters": {
"name": ["in", Object.keys(frappe.boot.party_account_types)],
}
};
};
}
};

View File

@ -35,8 +35,6 @@ class TestPaymentEntry(unittest.TestCase):
pe.cancel() pe.cancel()
self.assertFalse(self.get_gle(pe.name))
so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid") so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
self.assertEqual(so_advance_paid, 0) self.assertEqual(so_advance_paid, 0)
@ -124,7 +122,6 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(outstanding_amount, 0) self.assertEqual(outstanding_amount, 0)
pe.cancel() pe.cancel()
self.assertFalse(self.get_gle(pe.name))
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 100) self.assertEqual(outstanding_amount, 100)
@ -381,7 +378,6 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(outstanding_amount, 0) self.assertEqual(outstanding_amount, 0)
pe3.cancel() pe3.cancel()
self.assertFalse(self.get_gle(pe3.name))
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")) outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, -100) self.assertEqual(outstanding_amount, -100)

View File

@ -80,7 +80,7 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
paid_amt += d.amount paid_amt += d.amount
je.append('accounts', { je.append('accounts', {
'account': doc.references[0].account, 'account': doc.account,
'credit_in_account_currency': paid_amt 'credit_in_account_currency': paid_amt
}) })

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2015-12-15 22:23:24.745065", "creation": "2015-12-15 22:23:24.745065",
"doctype": "DocType", "doctype": "DocType",
@ -210,13 +211,14 @@
"label": "IBAN" "label": "IBAN"
}, },
{ {
"fetch_from": "bank_account.branch_code", "fetch_from": "bank.branch_code",
"fetch_if_empty": 1,
"fieldname": "branch_code", "fieldname": "branch_code",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"label": "Branch Code" "label": "Branch Code"
}, },
{ {
"fetch_from": "bank_account.swift_number", "fetch_from": "bank.swift_number",
"fieldname": "swift_number", "fieldname": "swift_number",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"label": "SWIFT Number" "label": "SWIFT Number"
@ -348,7 +350,8 @@
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"modified": "2020-03-28 16:07:31.960798", "links": [],
"modified": "2020-05-08 10:23:02.815237",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Request", "name": "Payment Request",

View File

@ -69,7 +69,7 @@ class PaymentRequest(Document):
elif self.payment_request_type == 'Inward': elif self.payment_request_type == 'Inward':
self.db_set('status', 'Requested') self.db_set('status', 'Requested')
send_mail = self.payment_gateway_validation() send_mail = self.payment_gateway_validation() if self.payment_gateway else None
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
@ -326,7 +326,7 @@ def make_payment_request(**args):
"reference_doctype": args.dt, "reference_doctype": args.dt,
"reference_name": args.dn, "reference_name": args.dn,
"party_type": args.get("party_type") or "Customer", "party_type": args.get("party_type") or "Customer",
"party": args.get("party") or ref_doc.customer, "party": args.get("party") or ref_doc.get("customer"),
"bank_account": bank_account "bank_account": bank_account
}) })

View File

@ -20,7 +20,7 @@ frappe.ui.form.on('Period Closing Voucher', {
}, },
refresh: function(frm) { refresh: function(frm) {
if(frm.doc.docstatus==1) { 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,

View File

@ -7,6 +7,8 @@ from frappe.utils import flt
from frappe import _ from frappe import _
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions,
get_dimension_filters)
class PeriodClosingVoucher(AccountsController): class PeriodClosingVoucher(AccountsController):
def validate(self): def validate(self):
@ -17,8 +19,9 @@ class PeriodClosingVoucher(AccountsController):
self.make_gl_entries() self.make_gl_entries()
def on_cancel(self): def on_cancel(self):
frappe.db.sql("""delete from `tabGL Entry` self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.name) from erpnext.accounts.general_ledger import make_reverse_gl_entries
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
def validate_account_head(self): def validate_account_head(self):
closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type") closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")
@ -49,7 +52,15 @@ class PeriodClosingVoucher(AccountsController):
def make_gl_entries(self): def make_gl_entries(self):
gl_entries = [] gl_entries = []
net_pl_balance = 0 net_pl_balance = 0
pl_accounts = self.get_pl_balances() dimension_fields = ['t1.cost_center']
accounting_dimensions = get_accounting_dimensions()
for dimension in accounting_dimensions:
dimension_fields.append('t1.{0}'.format(dimension))
dimension_filters, default_dimensions = get_dimension_filters()
pl_accounts = self.get_pl_balances(dimension_fields)
for acc in pl_accounts: for acc in pl_accounts:
if flt(acc.balance_in_company_currency): if flt(acc.balance_in_company_currency):
@ -65,34 +76,41 @@ class PeriodClosingVoucher(AccountsController):
if flt(acc.balance_in_account_currency) > 0 else 0, if flt(acc.balance_in_account_currency) > 0 else 0,
"credit": abs(flt(acc.balance_in_company_currency)) \ "credit": abs(flt(acc.balance_in_company_currency)) \
if flt(acc.balance_in_company_currency) > 0 else 0 if flt(acc.balance_in_company_currency) > 0 else 0
})) }, item=acc))
net_pl_balance += flt(acc.balance_in_company_currency) net_pl_balance += flt(acc.balance_in_company_currency)
if net_pl_balance: if net_pl_balance:
cost_center = frappe.db.get_value("Company", self.company, "cost_center") cost_center = frappe.db.get_value("Company", self.company, "cost_center")
gl_entries.append(self.get_gl_dict({ gl_entry = self.get_gl_dict({
"account": self.closing_account_head, "account": self.closing_account_head,
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0, "debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0, "debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0, "credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0, "credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
"cost_center": cost_center "cost_center": cost_center
})) })
for dimension in accounting_dimensions:
gl_entry.update({
dimension: default_dimensions.get(self.company, {}).get(dimension)
})
gl_entries.append(gl_entry)
from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.general_ledger import make_gl_entries
make_gl_entries(gl_entries) make_gl_entries(gl_entries)
def get_pl_balances(self): def get_pl_balances(self, dimension_fields):
"""Get balance for pl accounts""" """Get balance for pl accounts"""
return frappe.db.sql(""" return frappe.db.sql("""
select select
t1.account, t1.cost_center, t2.account_currency, t1.account, t2.account_currency, {dimension_fields},
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency, sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency,
sum(t1.debit) - sum(t1.credit) as balance_in_company_currency sum(t1.debit) - sum(t1.credit) as balance_in_company_currency
from `tabGL Entry` t1, `tabAccount` t2 from `tabGL Entry` t1, `tabAccount` t2
where t1.account = t2.name and t2.report_type = 'Profit and Loss' where t1.account = t2.name and t2.report_type = 'Profit and Loss'
and t2.docstatus < 2 and t2.company = %s and t2.docstatus < 2 and t2.company = %s
and t1.posting_date between %s and %s and t1.posting_date between %s and %s
group by t1.account, t1.cost_center group by t1.account, {dimension_fields}
""", (self.company, self.get("year_start_date"), self.posting_date), as_dict=1) """.format(dimension_fields = ', '.join(dimension_fields)), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)

View File

@ -1,123 +1,39 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-10-27 16:46:06.060930", "creation": "2017-10-27 16:46:06.060930",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [
"default",
"user"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default", "fieldname": "default",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0, "label": "Default"
"label": "Default",
"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,
"unique": 0
}, },
{ {
"allow_bulk_edit": 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": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "User", "label": "User",
"length": 0, "options": "User"
"no_copy": 0,
"options": "User",
"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,
"unique": 0
} }
], ],
"has_web_view": 0, "istable": 1,
"hide_heading": 0, "links": [],
"hide_toolbar": 0, "modified": "2020-05-01 09:46:47.599173",
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-23 17:13:16.005475",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "POS Profile User", "name": "POS Profile User",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [],
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1, "quick_entry": 1,
"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, "track_changes": 1
"track_seen": 0
} }

View File

@ -99,7 +99,7 @@ class PricingRule(Document):
self.same_item = 1 self.same_item = 1
def validate_max_discount(self): def validate_max_discount(self):
if self.rate_or_discount == "Discount Percentage" and self.items: if self.rate_or_discount == "Discount Percentage" and self.get("items"):
for d in self.items: for d in self.items:
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount") max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
if max_discount and flt(self.discount_percentage) > flt(max_discount): if max_discount and flt(self.discount_percentage) > flt(max_discount):

View File

@ -4,13 +4,19 @@
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, copy, json
from frappe import throw, _ import copy
import json
from six import string_types from six import string_types
from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
import frappe
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
from frappe import _, throw
from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
class MultiplePricingRuleConflict(frappe.ValidationError): pass class MultiplePricingRuleConflict(frappe.ValidationError): pass
@ -502,18 +508,16 @@ def get_pricing_rule_items(pr_doc):
return list(set(apply_on_data)) return list(set(apply_on_data))
def validate_coupon_code(coupon_name): def validate_coupon_code(coupon_name):
from frappe.utils import today,getdate coupon = frappe.get_doc("Coupon Code", coupon_name)
coupon=frappe.get_doc("Coupon Code",coupon_name)
if coupon.valid_from: if coupon.valid_from:
if coupon.valid_from > getdate(today()) : if coupon.valid_from > getdate(today()):
frappe.throw(_("Sorry,coupon code validity has not started")) frappe.throw(_("Sorry, this coupon code's validity has not started"))
elif coupon.valid_upto: elif coupon.valid_upto:
if coupon.valid_upto < getdate(today()) : if coupon.valid_upto < getdate(today()):
frappe.throw(_("Sorry,coupon code validity has expired")) frappe.throw(_("Sorry, this coupon code's validity has expired"))
elif coupon.used>=coupon.maximum_use: elif coupon.used >= coupon.maximum_use:
frappe.throw(_("Sorry,coupon code are exhausted")) frappe.throw(_("Sorry, this coupon code is no longer valid"))
else:
return
def update_coupon_code_count(coupon_name,transaction_type): def update_coupon_code_count(coupon_name,transaction_type):
coupon=frappe.get_doc("Coupon Code",coupon_name) coupon=frappe.get_doc("Coupon Code",coupon_name)

View File

@ -0,0 +1,39 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Process Deferred Accounting', {
setup: function(frm) {
frm.set_query("document_type", function() {
return {
filters: {
'name': ['in', ['Sales Invoice', 'Purchase Invoice']]
}
};
});
},
validate: function() {
return new Promise((resolve) => {
return frappe.db.get_single_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')
.then(value => {
if(value) {
frappe.throw(__('Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again'));
}
resolve(value);
});
});
},
end_date: function(frm) {
if (frm.doc.end_date && frm.doc.end_date < frm.doc.start_date) {
frappe.throw(__("End date cannot be before start date"));
}
},
onload: function(frm) {
if (frm.doc.posting_date && frm.doc.docstatus === 0) {
frm.set_value('start_date', frappe.datetime.add_months(frm.doc.posting_date, -1));
frm.set_value('end_date', frm.doc.posting_date);
}
}
});

View File

@ -0,0 +1,128 @@
{
"actions": [],
"autoname": "ACC-PDA-.#####",
"creation": "2019-11-04 18:01:23.454775",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"type",
"account",
"column_break_3",
"posting_date",
"start_date",
"end_date",
"amended_from"
],
"fields": [
{
"fieldname": "type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Type",
"options": "\nIncome\nExpense",
"reqd": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Process Deferred Accounting",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "start_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Service Start Date",
"reqd": 1
},
{
"fieldname": "end_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Service End Date",
"reqd": 1
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Posting Date",
"reqd": 1
},
{
"fieldname": "account",
"fieldtype": "Link",
"label": "Account",
"options": "Account"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-02-06 18:18:09.852844",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Deferred Accounting",
"owner": "Administrator",
"permissions": [
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"submit": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import erpnext
from frappe import _
from frappe.model.document import Document
from erpnext.accounts.general_ledger import make_reverse_gl_entries
from erpnext.accounts.deferred_revenue import convert_deferred_expense_to_expense, \
convert_deferred_revenue_to_income, build_conditions
class ProcessDeferredAccounting(Document):
def validate(self):
if self.end_date < self.start_date:
frappe.throw(_("End date cannot be before start date"))
def on_submit(self):
conditions = build_conditions(self.type, self.account, self.company)
if self.type == 'Income':
convert_deferred_revenue_to_income(self.name, self.start_date, self.end_date, conditions)
else:
convert_deferred_expense_to_expense(self.name, self.start_date, self.end_date, conditions)
def on_cancel(self):
self.ignore_linked_doctypes = ['GL Entry']
gl_entries = frappe.get_all('GL Entry', fields = ['*'],
filters={
'against_voucher_type': self.doctype,
'against_voucher': self.name
})
make_reverse_gl_entries(gl_entries=gl_entries)

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice, check_gl_entries
class TestProcessDeferredAccounting(unittest.TestCase):
def test_creation_of_ledger_entry_on_submit(self):
''' test creation of gl entries on submission of document '''
deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company")
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_account
item.no_of_months = 12
item.save()
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True)
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-10"
si.items[0].service_end_date = "2019-03-15"
si.items[0].deferred_revenue_account = deferred_account
si.save()
si.submit()
process_deferred_accounting = doc = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date="2019-01-01",
start_date="2019-01-01",
end_date="2019-01-31",
type="Income"
))
process_deferred_accounting.insert()
process_deferred_accounting.submit()
expected_gle = [
[deferred_account, 33.85, 0.0, "2019-01-31"],
["Sales - _TC", 0.0, 33.85, "2019-01-31"]
]
check_gl_entries(self, si.name, expected_gle, "2019-01-10")

View File

@ -382,11 +382,6 @@ function hide_fields(doc) {
cur_frm.refresh_fields(); cur_frm.refresh_fields();
} }
cur_frm.cscript.update_stock = function(doc, dt, dn) {
hide_fields(doc, dt, dn);
this.frm.fields_dict.items.grid.toggle_reqd("item_code", doc.update_stock? true: false)
}
cur_frm.fields_dict.cash_bank_account.get_query = function(doc) { cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
return { return {
filters: [ filters: [
@ -528,5 +523,10 @@ frappe.ui.form.on("Purchase Invoice", {
erpnext.buying.get_default_bom(frm); erpnext.buying.get_default_bom(frm);
} }
frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes"); frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
},
update_stock: function(frm) {
hide_fields(frm.doc);
frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock? true: false);
} }
}) })

View File

@ -14,7 +14,7 @@ from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
from erpnext.stock import get_warehouse_account_map from erpnext.stock import get_warehouse_account_map
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, make_reverse_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_on_hold_or_closed_status from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
@ -382,7 +382,7 @@ class PurchaseInvoice(BuyingController):
self.update_project() self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): def make_gl_entries(self, gl_entries=None):
if not self.grand_total: if not self.grand_total:
return return
if not gl_entries: if not gl_entries:
@ -391,21 +391,17 @@ class PurchaseInvoice(BuyingController):
if gl_entries: if gl_entries:
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes" update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), if self.docstatus == 1:
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost) make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False)
elif self.docstatus == 2:
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No": if update_outstanding == "No":
update_outstanding_amt(self.credit_to, "Supplier", self.supplier, update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name) self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) and self.auto_accounting_for_stock:
from erpnext.controllers.stock_controller import update_gl_entries_after
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time,
warehouses, items, company = self.company)
elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock: elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None): def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
@ -464,7 +460,7 @@ class PurchaseInvoice(BuyingController):
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency) }, self.party_account_currency, item=self)
) )
def make_item_gl_entries(self, gl_entries): def make_item_gl_entries(self, gl_entries):
@ -845,7 +841,7 @@ class PurchaseInvoice(BuyingController):
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency) }, self.party_account_currency, item=self)
) )
gl_entries.append( gl_entries.append(
@ -856,7 +852,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": self.base_paid_amount \ "credit_in_account_currency": self.base_paid_amount \
if bank_account_currency==self.company_currency else self.paid_amount, if bank_account_currency==self.company_currency else self.paid_amount,
"cost_center": self.cost_center "cost_center": self.cost_center
}, bank_account_currency) }, bank_account_currency, item=self)
) )
def make_write_off_gl_entry(self, gl_entries): def make_write_off_gl_entry(self, gl_entries):
@ -877,7 +873,7 @@ class PurchaseInvoice(BuyingController):
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency) }, self.party_account_currency, item=self)
) )
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
@ -887,7 +883,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": self.base_write_off_amount \ "credit_in_account_currency": self.base_write_off_amount \
if write_off_account_currency==self.company_currency else self.write_off_amount, if write_off_account_currency==self.company_currency else self.write_off_amount,
"cost_center": self.cost_center or self.write_off_cost_center "cost_center": self.cost_center or self.write_off_cost_center
}) }, item=self)
) )
def make_gle_for_rounding_adjustment(self, gl_entries): def make_gle_for_rounding_adjustment(self, gl_entries):
@ -906,8 +902,7 @@ class PurchaseInvoice(BuyingController):
"debit_in_account_currency": self.rounding_adjustment, "debit_in_account_currency": self.rounding_adjustment,
"debit": self.base_rounding_adjustment, "debit": self.base_rounding_adjustment,
"cost_center": self.cost_center or round_off_cost_center, "cost_center": self.cost_center or round_off_cost_center,
} }, item=self))
))
def on_cancel(self): def on_cancel(self):
super(PurchaseInvoice, self).on_cancel() super(PurchaseInvoice, self).on_cancel()
@ -934,6 +929,7 @@ class PurchaseInvoice(BuyingController):
frappe.db.set(self, 'status', 'Cancelled') frappe.db.set(self, 'status', 'Cancelled')
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference) unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
def update_project(self): def update_project(self):
project_list = [] project_list = []
@ -1025,6 +1021,40 @@ class PurchaseInvoice(BuyingController):
# calculate totals again after applying TDS # calculate totals again after applying TDS
self.calculate_taxes_and_totals() self.calculate_taxes_and_totals()
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get('amended_from'):
self.status = 'Draft'
return
precision = self.precision("outstanding_amount")
outstanding_amount = flt(self.outstanding_amount, precision)
due_date = getdate(self.due_date)
nowdate = getdate()
if not status:
if self.docstatus == 2:
status = "Cancelled"
elif self.docstatus == 1:
if outstanding_amount > 0 and due_date < nowdate:
self.status = "Overdue"
elif outstanding_amount > 0 and due_date >= nowdate:
self.status = "Unpaid"
#Check if outstanding amount is 0 due to debit note issued against invoice
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
self.status = "Debit Note Issued"
elif self.is_return == 1:
self.status = "Return"
elif outstanding_amount<=0:
self.status = "Paid"
else:
self.status = "Submitted"
else:
self.status = "Draft"
if update:
self.db_set('status', self.status, update_modified = update_modified)
def get_list_context(context=None): def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context) list_context = get_list_context(context)

View File

@ -86,6 +86,8 @@ class TestPurchaseInvoice(unittest.TestCase):
pe.submit() pe.submit()
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name) pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
pi_doc.load_from_db()
self.assertTrue(pi_doc.status, "Paid")
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel) self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
unlink_payment_on_cancel_of_invoice() unlink_payment_on_cancel_of_invoice()
@ -203,7 +205,9 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.insert() pi.insert()
pi.submit() pi.submit()
pi.load_from_db()
self.assertTrue(pi.status, "Unpaid")
self.check_gle_for_pi(pi.name) self.check_gle_for_pi(pi.name)
def check_gle_for_pi(self, pi): def check_gle_for_pi(self, pi):
@ -234,6 +238,9 @@ class TestPurchaseInvoice(unittest.TestCase):
pi = frappe.copy_doc(test_records[0]) pi = frappe.copy_doc(test_records[0])
pi.insert() pi.insert()
pi.load_from_db()
self.assertTrue(pi.status, "Draft")
pi.naming_series = 'TEST-' pi.naming_series = 'TEST-'
self.assertRaises(frappe.CannotChangeConstantError, pi.save) self.assertRaises(frappe.CannotChangeConstantError, pi.save)
@ -248,6 +255,8 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.get("taxes").pop(1) pi.get("taxes").pop(1)
pi.insert() pi.insert()
pi.submit() pi.submit()
pi.load_from_db()
self.assertTrue(pi.status, "Unpaid")
gl_entries = frappe.db.sql("""select account, debit, credit gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
@ -599,6 +608,11 @@ class TestPurchaseInvoice(unittest.TestCase):
# return entry # return entry
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1) pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
pi.load_from_db()
self.assertTrue(pi.status, "Debit Note Issued")
pi1.load_from_db()
self.assertTrue(pi1.status, "Return")
actual_qty_2 = get_qty_after_transaction() actual_qty_2 = get_qty_after_transaction()
self.assertEqual(actual_qty_1 - 2, actual_qty_2) self.assertEqual(actual_qty_1 - 2, actual_qty_2)
@ -771,6 +785,8 @@ class TestPurchaseInvoice(unittest.TestCase):
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1) pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
pi.load_from_db()
self.assertTrue(pi.status, "Return")
outstanding_amount = get_outstanding_amount(pi.doctype, outstanding_amount = get_outstanding_amount(pi.doctype,
pi.name, "Creditors - _TC", pi.supplier, "Supplier") pi.name, "Creditors - _TC", pi.supplier, "Supplier")

View File

@ -138,7 +138,6 @@
"row_id": 7 "row_id": 7
} }
], ],
"posting_date": "2013-02-03",
"supplier": "_Test Supplier", "supplier": "_Test Supplier",
"supplier_name": "_Test Supplier" "supplier_name": "_Test Supplier"
}, },
@ -204,7 +203,6 @@
"tax_amount": 150.0 "tax_amount": 150.0
} }
], ],
"posting_date": "2013-02-03",
"supplier": "_Test Supplier", "supplier": "_Test Supplier",
"supplier_name": "_Test Supplier" "supplier_name": "_Test Supplier"
} }

View File

@ -26,16 +26,24 @@ frappe.ui.form.on("Sales Invoice", {
&& !frm.doc.is_return && !frm.doc.ewaybill) { && !frm.doc.is_return && !frm.doc.ewaybill) {
frm.add_custom_button('E-Way Bill JSON', () => { frm.add_custom_button('E-Way Bill JSON', () => {
var w = window.open( frappe.call({
frappe.urllib.get_full_url( method: 'erpnext.regional.india.utils.generate_ewb_json',
"/api/method/erpnext.regional.india.utils.generate_ewb_json?" args: {
+ "dt=" + encodeURIComponent(frm.doc.doctype) 'dt': frm.doc.doctype,
+ "&dn=" + encodeURIComponent(frm.doc.name) 'dn': [frm.doc.name]
) },
); callback: function(r) {
if (!w) { if (r.message) {
frappe.msgprint(__("Please enable pop-ups")); return; const args = {
cmd: 'erpnext.regional.india.utils.download_ewb_json',
data: r.message,
docname: frm.doc.name
};
open_url_post(frappe.request.url, args);
} }
}
});
}, __("Create")); }, __("Create"));
} }
} }

View File

@ -16,17 +16,23 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
} }
} }
var w = window.open( frappe.call({
frappe.urllib.get_full_url( method: 'erpnext.regional.india.utils.generate_ewb_json',
"/api/method/erpnext.regional.india.utils.generate_ewb_json?" args: {
+ "dt=" + encodeURIComponent(doclist.doctype) 'dt': doclist.doctype,
+ "&dn=" + encodeURIComponent(docnames) 'dn': docnames
) },
); callback: function(r) {
if (!w) { if (r.message) {
frappe.msgprint(__("Please enable pop-ups")); return; const args = {
cmd: 'erpnext.regional.india.utils.download_ewb_json',
data: r.message,
docname: docnames
};
open_url_post(frappe.request.url, args);
} }
}
});
}; };
doclist.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false); doclist.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);

View File

@ -345,7 +345,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
set_dynamic_labels: function() { set_dynamic_labels: function() {
this._super(); this._super();
this.hide_fields(this.frm.doc); this.frm.events.hide_fields(this.frm)
}, },
items_on_form_rendered: function() { items_on_form_rendered: function() {
@ -404,7 +404,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
if(r.message && r.message.print_format) { if(r.message && r.message.print_format) {
me.frm.pos_print_format = r.message.print_format; me.frm.pos_print_format = r.message.print_format;
} }
me.frm.script_manager.trigger("update_stock"); me.frm.trigger("update_stock");
if(me.frm.doc.taxes_and_charges) { if(me.frm.doc.taxes_and_charges) {
me.frm.script_manager.trigger("taxes_and_charges"); me.frm.script_manager.trigger("taxes_and_charges");
} }
@ -446,35 +446,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
// for backward compatibility: combine new and previous states // for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm})); $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
// Hide Fields
// ------------
cur_frm.cscript.hide_fields = function(doc) {
var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
hide_field(parent_fields);
} else {
for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]);
}
}
// India related fields
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
else hide_field(['c_form_applicable', 'c_form_no']);
this.frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
cur_frm.refresh_fields();
}
cur_frm.cscript.update_stock = function(doc, dt, dn) {
cur_frm.cscript.hide_fields(doc, dt, dn);
this.frm.fields_dict.items.grid.toggle_reqd("item_code", doc.update_stock? true: false)
}
cur_frm.cscript['Make Delivery Note'] = function() { cur_frm.cscript['Make Delivery Note'] = function() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_delivery_note", method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_delivery_note",
@ -719,6 +690,12 @@ frappe.ui.form.on('Sales Invoice', {
frm.redemption_conversion_factor = null; frm.redemption_conversion_factor = null;
}, },
update_stock: function(frm, dt, dn) {
frm.events.hide_fields(frm);
frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock);
frm.trigger('reset_posting_time');
},
redeem_loyalty_points: function(frm) { redeem_loyalty_points: function(frm) {
frm.events.get_loyalty_details(frm); frm.events.get_loyalty_details(frm);
}, },
@ -742,6 +719,29 @@ frappe.ui.form.on('Sales Invoice', {
} }
}, },
hide_fields: function(frm) {
let doc = frm.doc;
var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
hide_field(parent_fields);
} else {
for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]);
}
}
// India related fields
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
else hide_field(['c_form_applicable', 'c_form_no']);
frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
frm.refresh_fields();
},
get_loyalty_details: function(frm) { get_loyalty_details: function(frm) {
if (frm.doc.customer && frm.doc.redeem_loyalty_points) { if (frm.doc.customer && frm.doc.redeem_loyalty_points) {
frappe.call({ frappe.call({
@ -924,7 +924,7 @@ var get_healthcare_services_to_invoice = function(frm) {
if(patient && patient!=selected_patient){ if(patient && patient!=selected_patient){
selected_patient = patient; selected_patient = patient;
var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice"; var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice";
var args = {patient: patient}; var args = {patient: patient, company: frm.doc.company};
var columns = (["service", "reference_name", "reference_type"]); var columns = (["service", "reference_name", "reference_type"]);
get_healthcare_items(frm, true, $results, $placeholder, method, args, columns); get_healthcare_items(frm, true, $results, $placeholder, method, args, columns);
} }
@ -1068,7 +1068,11 @@ var get_drugs_to_invoice = function(frm) {
description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.', description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.',
get_query: function(doc) { get_query: function(doc) {
return { return {
filters: { patient: dialog.get_value("patient"), docstatus: 1 } filters: {
patient: dialog.get_value("patient"),
company: frm.doc.company,
docstatus: 1
}
}; };
} }
}, },

View File

@ -149,9 +149,9 @@
"edit_printing_settings", "edit_printing_settings",
"letter_head", "letter_head",
"group_same_items", "group_same_items",
"language",
"column_break_84",
"select_print_heading", "select_print_heading",
"column_break_84",
"language",
"more_information", "more_information",
"inter_company_invoice_reference", "inter_company_invoice_reference",
"is_internal_customer", "is_internal_customer",
@ -398,7 +398,7 @@
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"fieldname": "po_no", "fieldname": "po_no",
"fieldtype": "Data", "fieldtype": "Small Text",
"label": "Customer's Purchase Order", "label": "Customer's Purchase Order",
"no_copy": 1, "no_copy": 1,
"print_hide": 1 "print_hide": 1
@ -1579,7 +1579,7 @@
"idx": 181, "idx": 181,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-04-17 12:38:41.435728", "modified": "2020-05-19 17:00:57.208696",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -7,7 +7,6 @@ import frappe.defaults
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
from frappe import _, msgprint, throw from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.controllers.stock_controller import update_gl_entries_after
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.accounts.doctype.sales_invoice.pos import update_multi_mode_option from erpnext.accounts.doctype.sales_invoice.pos import update_multi_mode_option
@ -282,6 +281,8 @@ class SalesInvoice(SellingController):
if "Healthcare" in active_domains: if "Healthcare" in active_domains:
manage_invoice_submit_cancel(self, "on_cancel") manage_invoice_submit_cancel(self, "on_cancel")
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
def update_status_updater_args(self): def update_status_updater_args(self):
if cint(self.update_stock): if cint(self.update_stock):
self.status_updater.append({ self.status_updater.append({
@ -717,7 +718,9 @@ class SalesInvoice(SellingController):
if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1: if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1:
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note)) throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): def make_gl_entries(self, gl_entries=None):
from erpnext.accounts.general_ledger import make_reverse_gl_entries
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if not gl_entries: if not gl_entries:
gl_entries = self.get_gl_entries() gl_entries = self.get_gl_entries()
@ -729,23 +732,19 @@ class SalesInvoice(SellingController):
update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account or update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account or
cint(self.redeem_loyalty_points)) else "Yes" cint(self.redeem_loyalty_points)) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), if self.docstatus == 1:
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost) make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False)
elif self.docstatus == 2:
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No": if update_outstanding == "No":
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
update_outstanding_amt(self.debit_to, "Customer", self.customer, update_outstanding_amt(self.debit_to, "Customer", self.customer,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name) self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) \
and cint(auto_accounting_for_stock):
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time,
warehouses, items, company = self.company)
elif self.docstatus == 2 and cint(self.update_stock) \ elif self.docstatus == 2 and cint(self.update_stock) \
and cint(auto_accounting_for_stock): and cint(auto_accounting_for_stock):
from erpnext.accounts.general_ledger import delete_gl_entries make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None): def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import merge_similar_entries from erpnext.accounts.general_ledger import merge_similar_entries
@ -792,7 +791,7 @@ class SalesInvoice(SellingController):
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency) }, self.party_account_currency, item=self)
) )
def make_tax_gl_entries(self, gl_entries): def make_tax_gl_entries(self, gl_entries):
@ -809,7 +808,7 @@ class SalesInvoice(SellingController):
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))), flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
"cost_center": tax.cost_center "cost_center": tax.cost_center
}, account_currency) }, account_currency, item=tax)
) )
def make_item_gl_entries(self, gl_entries): def make_item_gl_entries(self, gl_entries):
@ -829,7 +828,7 @@ class SalesInvoice(SellingController):
for gle in fixed_asset_gl_entries: for gle in fixed_asset_gl_entries:
gle["against"] = self.customer gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle)) gl_entries.append(self.get_gl_dict(gle, item=item))
asset.db_set("disposal_date", self.posting_date) asset.db_set("disposal_date", self.posting_date)
asset.set_status("Sold" if self.docstatus==1 else None) asset.set_status("Sold" if self.docstatus==1 else None)
@ -867,7 +866,7 @@ class SalesInvoice(SellingController):
"against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}) }, item=self)
) )
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
@ -876,7 +875,7 @@ class SalesInvoice(SellingController):
"against": self.customer, "against": self.customer,
"debit": self.loyalty_amount, "debit": self.loyalty_amount,
"remark": "Loyalty Points redeemed by the customer" "remark": "Loyalty Points redeemed by the customer"
}) }, item=self)
) )
def make_pos_gl_entries(self, gl_entries): def make_pos_gl_entries(self, gl_entries):
@ -897,7 +896,7 @@ class SalesInvoice(SellingController):
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency) }, self.party_account_currency, item=self)
) )
payment_mode_account_currency = get_account_currency(payment_mode.account) payment_mode_account_currency = get_account_currency(payment_mode.account)
@ -910,7 +909,7 @@ class SalesInvoice(SellingController):
if payment_mode_account_currency==self.company_currency \ if payment_mode_account_currency==self.company_currency \
else payment_mode.amount, else payment_mode.amount,
"cost_center": self.cost_center "cost_center": self.cost_center
}, payment_mode_account_currency) }, payment_mode_account_currency, item=self)
) )
def make_gle_for_change_amount(self, gl_entries): def make_gle_for_change_amount(self, gl_entries):
@ -928,7 +927,7 @@ class SalesInvoice(SellingController):
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency) }, self.party_account_currency, item=self)
) )
gl_entries.append( gl_entries.append(
@ -937,7 +936,7 @@ class SalesInvoice(SellingController):
"against": self.customer, "against": self.customer,
"credit": self.base_change_amount, "credit": self.base_change_amount,
"cost_center": self.cost_center "cost_center": self.cost_center
}) }, item=self)
) )
else: else:
frappe.throw(_("Select change amount account"), title="Mandatory Field") frappe.throw(_("Select change amount account"), title="Mandatory Field")
@ -961,7 +960,7 @@ class SalesInvoice(SellingController):
"against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency) }, self.party_account_currency, item=self)
) )
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
@ -972,7 +971,7 @@ class SalesInvoice(SellingController):
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))), else flt(self.write_off_amount, self.precision("write_off_amount"))),
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center "cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
}, write_off_account_currency) }, write_off_account_currency, item=self)
) )
def make_gle_for_rounding_adjustment(self, gl_entries): def make_gle_for_rounding_adjustment(self, gl_entries):
@ -989,8 +988,7 @@ class SalesInvoice(SellingController):
"credit": flt(self.base_rounding_adjustment, "credit": flt(self.base_rounding_adjustment,
self.precision("base_rounding_adjustment")), self.precision("base_rounding_adjustment")),
"cost_center": self.cost_center or round_off_cost_center, "cost_center": self.cost_center or round_off_cost_center,
} }, item=self))
))
def update_billing_status_in_dn(self, update_modified=True): def update_billing_status_in_dn(self, update_modified=True):
updated_delivery_notes = [] updated_delivery_notes = []

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest, copy, time import unittest, copy, time
from frappe.utils import nowdate, flt, getdate, cint, add_days from frappe.utils import nowdate, flt, getdate, cint, add_days, add_months
from frappe.model.dynamic_links import get_dynamic_link_map from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
@ -364,7 +364,7 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select * from `tabGL Entry` gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name) where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle) self.assertTrue(gle)
def test_tax_calculation_with_multiple_items(self): def test_tax_calculation_with_multiple_items(self):
si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True) si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True)
@ -678,14 +678,15 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select * from `tabGL Entry` gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name) where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle) self.assertTrue(gle)
def test_pos_gl_entry_with_perpetual_inventory(self): def test_pos_gl_entry_with_perpetual_inventory(self):
make_pos_profile() make_pos_profile()
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1") pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True) pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1 pos.is_pos = 1
pos.update_stock = 1 pos.update_stock = 1
@ -766,9 +767,13 @@ class TestSalesInvoice(unittest.TestCase):
def test_pos_change_amount(self): def test_pos_change_amount(self):
make_pos_profile() make_pos_profile()
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1") pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True) pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1 pos.is_pos = 1
pos.update_stock = 1 pos.update_stock = 1
@ -787,8 +792,15 @@ class TestSalesInvoice(unittest.TestCase):
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
pos_profile = make_pos_profile() pos_profile = make_pos_profile()
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True) pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
item_code= "_Test FG Item",
warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1 pos.is_pos = 1
pos.update_stock = 1 pos.update_stock = 1
@ -891,11 +903,9 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select * from `tabGL Entry` gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name) where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle) self.assertTrue(gle)
frappe.db.sql("delete from `tabPOS Profile`") frappe.db.sql("delete from `tabPOS Profile`")
si.delete()
def test_pos_si_without_payment(self): def test_pos_si_without_payment(self):
set_perpetual_inventory() set_perpetual_inventory()
@ -1012,9 +1022,6 @@ class TestSalesInvoice(unittest.TestCase):
si.cancel() si.cancel()
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_name=%s""", si.name))
def test_serialized(self): def test_serialized(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@ -1230,7 +1237,7 @@ class TestSalesInvoice(unittest.TestCase):
gle = frappe.db.sql("""select name from `tabGL Entry` gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name) where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle) self.assertTrue(gle)
def test_invalid_currency(self): def test_invalid_currency(self):
# Customer currency = USD # Customer currency = USD
@ -1714,37 +1721,76 @@ class TestSalesInvoice(unittest.TestCase):
si.submit() si.submit()
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
convert_deferred_revenue_to_income(start_date="2019-01-01", end_date="2019-01-31")
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
start_date="2019-01-01",
end_date="2019-03-31",
type="Income",
company="_Test Company"
))
pda1.insert()
pda1.submit()
expected_gle = [ expected_gle = [
[deferred_account, 33.85, 0.0, "2019-01-31"], [deferred_account, 33.85, 0.0, "2019-01-31"],
["Sales - _TC", 0.0, 33.85, "2019-01-31"] ["Sales - _TC", 0.0, 33.85, "2019-01-31"],
]
self.check_gl_entries(si.name, expected_gle, "2019-01-10")
convert_deferred_revenue_to_income(start_date="2019-01-01", end_date="2019-03-31")
expected_gle = [
[deferred_account, 43.08, 0.0, "2019-02-28"], [deferred_account, 43.08, 0.0, "2019-02-28"],
["Sales - _TC", 0.0, 43.08, "2019-02-28"], ["Sales - _TC", 0.0, 43.08, "2019-02-28"],
[deferred_account, 23.07, 0.0, "2019-03-15"], [deferred_account, 23.07, 0.0, "2019-03-15"],
["Sales - _TC", 0.0, 23.07, "2019-03-15"] ["Sales - _TC", 0.0, 23.07, "2019-03-15"]
] ]
self.check_gl_entries(si.name, expected_gle, "2019-01-31") check_gl_entries(self, si.name, expected_gle, "2019-01-30")
def check_gl_entries(self, voucher_no, expected_gle, posting_date): def test_deferred_error_email(self):
gl_entries = frappe.db.sql("""select account, debit, credit, posting_date deferred_account = create_account(account_name="Deferred Revenue",
from `tabGL Entry` parent_account="Current Liabilities - _TC", company="_Test Company")
where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s
order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1)
for i, gle in enumerate(gl_entries): item = create_item("_Test Item for Deferred Accounting")
self.assertEqual(expected_gle[i][0], gle.account) item.enable_deferred_revenue = 1
self.assertEqual(expected_gle[i][1], gle.debit) item.deferred_revenue_account = deferred_account
self.assertEqual(expected_gle[i][2], gle.credit) item.no_of_months = 12
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) item.save()
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True)
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-10"
si.items[0].service_end_date = "2019-03-15"
si.items[0].deferred_revenue_account = deferred_account
si.save()
si.submit()
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.acc_frozen_upto = '2019-01-31'
acc_settings.save()
pda = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
start_date="2019-01-01",
end_date="2019-03-31",
type="Income",
company="_Test Company"
))
pda.insert()
pda.submit()
email = frappe.db.sql(""" select name from `tabEmail Queue`
where message like %(txt)s """, {
'txt': "%%%s%%" % "Error while processing deferred accounting for {0}".format(pda.name)
})
self.assertTrue(email)
acc_settings.load_from_db()
acc_settings.acc_frozen_upto = None
acc_settings.save()
def test_inter_company_transaction(self): def test_inter_company_transaction(self):
@ -1892,7 +1938,7 @@ class TestSalesInvoice(unittest.TestCase):
si.submit() si.submit()
data = get_ewb_data("Sales Invoice", si.name) data = get_ewb_data("Sales Invoice", [si.name])
self.assertEqual(data['version'], '1.0.1118') self.assertEqual(data['version'], '1.0.1118')
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR') self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
@ -1905,6 +1951,18 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234') self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000) self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s
order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1)
for i, gle in enumerate(gl_entries):
doc.assertEqual(expected_gle[i][0], gle.account)
doc.assertEqual(expected_gle[i][1], gle.debit)
doc.assertEqual(expected_gle[i][2], gle.credit)
doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
def test_item_tax_validity(self): def test_item_tax_validity(self):
item = frappe.get_doc("Item", "_Test Item 2") item = frappe.get_doc("Item", "_Test Item 2")

View File

@ -168,18 +168,20 @@ class ShareTransfer(Document):
return 'Outside' return 'Outside'
def folio_no_validation(self): def folio_no_validation(self):
shareholders = ['from_shareholder', 'to_shareholder'] shareholder_fields = ['from_shareholder', 'to_shareholder']
shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) is not ''] for shareholder_field in shareholder_fields:
for shareholder in shareholders: shareholder_name = self.get(shareholder_field)
doc = self.get_shareholder_doc(self.get(shareholder)) if not shareholder_name:
continue
doc = self.get_shareholder_doc(shareholder_name)
if doc.company != self.company: if doc.company != self.company:
frappe.throw(_('The shareholder does not belong to this company')) frappe.throw(_('The shareholder does not belong to this company'))
if not doc.folio_no: if not doc.folio_no:
doc.folio_no = self.from_folio_no \ doc.folio_no = self.from_folio_no \
if (shareholder == 'from_shareholder') else self.to_folio_no if (shareholder_field == 'from_shareholder') else self.to_folio_no
doc.save() doc.save()
else: else:
if doc.folio_no and doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no): if doc.folio_no and doc.folio_no != (self.from_folio_no if (shareholder_field == 'from_shareholder') else self.to_folio_no):
frappe.throw(_('The folio numbers are not matching')) frappe.throw(_('The folio numbers are not matching'))
def autoname_folio(self, shareholder, is_company=False): def autoname_folio(self, shareholder, is_company=False):

View File

@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
"rate": tax_rate_detail.tax_withholding_rate, "rate": tax_rate_detail.tax_withholding_rate,
"threshold": tax_rate_detail.single_threshold, "threshold": tax_rate_detail.single_threshold,
"cumulative_threshold": tax_rate_detail.cumulative_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold,
"description": tax_withholding.category_name "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category
}) })
def get_tax_withholding_rates(tax_withholding, fiscal_year): def get_tax_withholding_rates(tax_withholding, fiscal_year):

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe.utils import flt, cstr, cint, comma_and from frappe.utils import flt, cstr, cint, comma_and, today, getdate, formatdate, now
from frappe import _ from frappe import _
from erpnext.accounts.utils import get_stock_and_account_balance from erpnext.accounts.utils import get_stock_and_account_balance
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
@ -15,17 +15,17 @@ class ClosedAccountingPeriod(frappe.ValidationError): pass
class StockAccountInvalidTransaction(frappe.ValidationError): pass class StockAccountInvalidTransaction(frappe.ValidationError): pass
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False): def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes'):
if gl_map: if gl_map:
if not cancel: if not cancel:
validate_accounting_period(gl_map) validate_accounting_period(gl_map)
gl_map = process_gl_map(gl_map, merge_entries) gl_map = process_gl_map(gl_map, merge_entries)
if gl_map and len(gl_map) > 1: if gl_map and len(gl_map) > 1:
save_entries(gl_map, adv_adj, update_outstanding, from_repost) save_entries(gl_map, adv_adj, update_outstanding)
else: else:
frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.")) frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."))
else: else:
delete_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding) make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
def validate_accounting_period(gl_map): def validate_accounting_period(gl_map):
accounting_periods = frappe.db.sql(""" SELECT accounting_periods = frappe.db.sql(""" SELECT
@ -119,33 +119,36 @@ def check_if_in_list(gle, gl_map, dimensions=None):
if same_head: if same_head:
return e return e
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): def save_entries(gl_map, adv_adj, update_outstanding):
if not from_repost:
validate_cwip_accounts(gl_map) validate_cwip_accounts(gl_map)
round_off_debit_credit(gl_map) round_off_debit_credit(gl_map)
if gl_map:
check_freezing_date(gl_map[0]["posting_date"], adv_adj)
for entry in gl_map: for entry in gl_map:
make_entry(entry, adv_adj, update_outstanding, from_repost) make_entry(entry, adv_adj, update_outstanding)
# check against budget # check against budget
if not from_repost:
validate_expense_against_budget(entry) validate_expense_against_budget(entry)
if not from_repost:
validate_account_for_perpetual_inventory(gl_map) validate_account_for_perpetual_inventory(gl_map)
def make_entry(args, adv_adj, update_outstanding, from_repost=False): def make_entry(args, adv_adj, update_outstanding):
gle = frappe.new_doc("GL Entry") gle = frappe.new_doc("GL Entry")
gle.update(args) gle.update(args)
gle.flags.ignore_permissions = 1 gle.flags.ignore_permissions = 1
gle.flags.from_repost = from_repost
gle.validate() gle.validate()
gle.db_insert() gle.db_insert()
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) gle.run_method("on_update_with_args", adv_adj, update_outstanding)
gle.flags.ignore_validate = True gle.flags.ignore_validate = True
gle.submit() gle.submit()
# check against budget
validate_expense_against_budget(args)
def validate_account_for_perpetual_inventory(gl_map): def validate_account_for_perpetual_inventory(gl_map):
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)): if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)):
account_list = [gl_entries.account for gl_entries in gl_map] account_list = [gl_entries.account for gl_entries in gl_map]
@ -169,33 +172,33 @@ def validate_account_for_perpetual_inventory(gl_map):
.format(account), StockAccountInvalidTransaction) .format(account), StockAccountInvalidTransaction)
# This has been comment for a temporary, will add this code again on release of immutable ledger # This has been comment for a temporary, will add this code again on release of immutable ledger
# elif account_bal != stock_bal: elif account_bal != stock_bal:
# precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
# currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency")) currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
# diff = flt(stock_bal - account_bal, precision) diff = flt(stock_bal - account_bal, precision)
# error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format( error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
# stock_bal, account_bal, frappe.bold(account)) stock_bal, account_bal, frappe.bold(account))
# error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff)) error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
# stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account") stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
# db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency') db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
# db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency') db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
# journal_entry_args = { journal_entry_args = {
# 'accounts':[ 'accounts':[
# {'account': account, db_or_cr_warehouse_account : abs(diff)}, {'account': account, db_or_cr_warehouse_account : abs(diff)},
# {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }] {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
# } }
# frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution), frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
# raise_exception=StockValueAndAccountBalanceOutOfSync, raise_exception=StockValueAndAccountBalanceOutOfSync,
# title=_('Values Out Of Sync'), title=_('Values Out Of Sync'),
# primary_action={ primary_action={
# 'label': _('Make Journal Entry'), 'label': _('Make Journal Entry'),
# 'client_action': 'erpnext.route_to_adjustment_jv', 'client_action': 'erpnext.route_to_adjustment_jv',
# 'args': journal_entry_args 'args': journal_entry_args
# }) })
def validate_cwip_accounts(gl_map): def validate_cwip_accounts(gl_map):
cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")]) cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
@ -282,31 +285,65 @@ def get_round_off_account_and_cost_center(company):
return round_off_account, round_off_cost_center return round_off_account, round_off_cost_center
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None, def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes"): adv_adj=False, update_outstanding="Yes"):
"""
from erpnext.accounts.doctype.gl_entry.gl_entry import validate_balance_type, \ Get original gl entries of the voucher
check_freezing_date, update_outstanding_amt, validate_frozen_account and make reverse gl entries by swapping debit and credit
"""
if not gl_entries: if not gl_entries:
gl_entries = frappe.db.sql(""" gl_entries = frappe.get_all("GL Entry",
select account, posting_date, party_type, party, cost_center, fiscal_year,voucher_type, fields = ["*"],
voucher_no, against_voucher_type, against_voucher, cost_center, company filters = {
from `tabGL Entry` "voucher_type": voucher_type,
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True) "voucher_no": voucher_no,
"is_cancelled": 0
})
if gl_entries: if gl_entries:
set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
check_freezing_date(gl_entries[0]["posting_date"], adv_adj) check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""",
(voucher_type or gl_entries[0]["voucher_type"], voucher_no or gl_entries[0]["voucher_no"]))
for entry in gl_entries: for entry in gl_entries:
validate_frozen_account(entry["account"], adv_adj) entry['name'] = None
validate_balance_type(entry["account"], adv_adj) debit = entry.get('debit', 0)
if not adv_adj: credit = entry.get('credit', 0)
validate_expense_against_budget(entry)
if entry.get("against_voucher") and update_outstanding == 'Yes' and not adv_adj: debit_in_account_currency = entry.get('debit_in_account_currency', 0)
update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"), credit_in_account_currency = entry.get('credit_in_account_currency', 0)
entry.get("against_voucher"), on_cancel=True)
entry['debit'] = credit
entry['credit'] = debit
entry['debit_in_account_currency'] = credit_in_account_currency
entry['credit_in_account_currency'] = debit_in_account_currency
entry['remarks'] = "On cancellation of " + entry['voucher_no']
entry['is_cancelled'] = 1
entry['posting_date'] = today()
if entry['debit'] or entry['credit']:
make_entry(entry, adv_adj, "Yes")
def check_freezing_date(posting_date, adv_adj=False):
"""
Nobody can do GL Entries where posting date is before freezing date
except authorized person
"""
if not adv_adj:
acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
if acc_frozen_upto:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if getdate(posting_date) <= getdate(acc_frozen_upto) \
and not frozen_accounts_modifier in frappe.get_roles():
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
def set_as_cancel(voucher_type, voucher_no):
"""
Set is_cancelled=1 in all original gl entries for the voucher
"""
frappe.db.sql("""update `tabGL Entry` set is_cancelled = 1,
modified=%s, modified_by=%s
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
(now(), frappe.session.user, voucher_type, voucher_no))

View File

@ -0,0 +1,51 @@
{
"allow_roles": [
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
}
],
"creation": "2020-05-13 19:03:32.564049",
"docstatus": 0,
"doctype": "Module Onboarding",
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
"idx": 0,
"is_complete": 0,
"modified": "2020-05-14 22:11:06.475938",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts",
"owner": "Administrator",
"steps": [
{
"step": "Chart Of Accounts"
},
{
"step": "Setup Taxes"
},
{
"step": "Create a Product"
},
{
"step": "Create a Supplier"
},
{
"step": "Create Your First Purchase Invoice"
},
{
"step": "Create a Customer"
},
{
"step": "Create Your First Sales Invoice"
},
{
"step": "Configure Account Settings"
}
],
"subtitle": "Accounts, invoices and taxation.",
"success_message": "The Accounts module is now set up!",
"title": "Let's Setup Your Accounts and Taxes.",
"user_can_dismiss": 1
}

View File

@ -0,0 +1,20 @@
{
"action": "Go to Page",
"creation": "2020-05-13 19:58:20.928127",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 17:40:28.410447",
"modified_by": "Administrator",
"name": "Chart Of Accounts",
"owner": "Administrator",
"path": "Tree/Account",
"reference_document": "Account",
"show_full_form": 0,
"title": "Review Chart Of Accounts",
"validate_action": 0
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:53:00.876946",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 1,
"is_skipped": 0,
"modified": "2020-05-14 18:06:25.212923",
"modified_by": "Administrator",
"name": "Configure Account Settings",
"owner": "Administrator",
"reference_document": "Accounts Settings",
"show_full_form": 1,
"title": "Configure Account Settings",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:46:41.831517",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 17:46:41.831517",
"modified_by": "Administrator",
"name": "Create a Customer",
"owner": "Administrator",
"reference_document": "Customer",
"show_full_form": 0,
"title": "Create a Customer",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:45:28.554605",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 17:45:28.554605",
"modified_by": "Administrator",
"name": "Create a Product",
"owner": "Administrator",
"reference_document": "Item",
"show_full_form": 0,
"title": "Create a Product",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 22:09:10.043554",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 22:09:10.043554",
"modified_by": "Administrator",
"name": "Create a Supplier",
"owner": "Administrator",
"reference_document": "Supplier",
"show_full_form": 0,
"title": "Create a Supplier",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 22:10:07.049704",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 22:10:07.049704",
"modified_by": "Administrator",
"name": "Create Your First Purchase Invoice",
"owner": "Administrator",
"reference_document": "Purchase Invoice",
"show_full_form": 1,
"title": "Create Your First Purchase Invoice ",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:48:21.019019",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 17:48:21.019019",
"modified_by": "Administrator",
"name": "Create Your First Sales Invoice",
"owner": "Administrator",
"reference_document": "Sales Invoice",
"show_full_form": 1,
"title": "Create Your First Sales Invoice ",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-13 19:29:43.844463",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 17:40:16.014413",
"modified_by": "Administrator",
"name": "Setup Taxes",
"owner": "Administrator",
"reference_document": "Sales Taxes and Charges Template",
"show_full_form": 1,
"title": "Lets create a Tax Template for Sales ",
"validate_action": 0
}

View File

@ -465,23 +465,25 @@ def get_timeline_data(doctype, name):
from frappe.desk.form.load import get_communication_data from frappe.desk.form.load import get_communication_data
out = {} out = {}
fields = 'date(creation), count(name)' fields = 'creation, count(*)'
after = add_years(None, -1).strftime('%Y-%m-%d') after = add_years(None, -1).strftime('%Y-%m-%d')
group_by='group by date(creation)' group_by='group by Date(creation)'
data = get_communication_data(doctype, name, after=after, group_by='group by date(creation)', data = get_communication_data(doctype, name, after=after, group_by='group by creation',
fields='date(C.creation) as creation, count(C.name)',as_dict=False) fields='C.creation as creation, count(C.name)',as_dict=False)
# fetch and append data from Activity Log # fetch and append data from Activity Log
data += frappe.db.sql("""select {fields} data += frappe.db.sql("""select {fields}
from `tabActivity Log` from `tabActivity Log`
where (reference_doctype="{doctype}" and reference_name="{name}") where (reference_doctype=%(doctype)s and reference_name=%(name)s)
or (timeline_doctype in ("{doctype}") and timeline_name="{name}") or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s)
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}") or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s)
and status!='Success' and creation > {after} and status!='Success' and creation > {after}
{group_by} order by creation desc {group_by} order by creation desc
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields, """.format(fields=fields, group_by=group_by, after=after), {
group_by=group_by, after=after), as_dict=False) "doctype": doctype,
"name": name
}, as_dict=False)
timeline_items = dict(data) timeline_items = dict(data)
@ -600,10 +602,12 @@ def get_party_shipping_address(doctype, name):
else: else:
return '' return ''
def get_partywise_advanced_payment_amount(party_type, posting_date = None): def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None):
cond = "1=1" cond = "1=1"
if posting_date: if posting_date:
cond = "posting_date <= '{0}'".format(posting_date) cond = "posting_date <= '{0}'".format(posting_date)
if company:
cond += "and company = '{0}'".format(company)
data = frappe.db.sql(""" SELECT party, sum({0}) as amount data = frappe.db.sql(""" SELECT party, sum({0}) as amount
FROM `tabGL Entry` FROM `tabGL Entry`

View File

@ -534,7 +534,7 @@ class ReceivablePayableReport(object):
def get_ageing_data(self, entry_date, row): def get_ageing_data(self, entry_date, row):
# [0-30, 30-60, 60-90, 90-120, 120-above] # [0-30, 30-60, 60-90, 90-120, 120-above]
row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0 row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
if not (self.age_as_on and entry_date): if not (self.age_as_on and entry_date):
return return
@ -546,7 +546,7 @@ class ReceivablePayableReport(object):
self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = 30, 60, 90, 120 self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = 30, 60, 90, 120
for i, days in enumerate([self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]): for i, days in enumerate([self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]):
if row.age <= days: if cint(row.age) <= cint(days):
index = i index = i
break break

View File

@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.get_party_total(args) self.get_party_total(args)
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
self.filters.report_date) or {} self.filters.report_date, self.filters.company) or {}
for party, party_dict in iteritems(self.party_total): for party, party_dict in iteritems(self.party_total):
if party_dict.outstanding == 0: if party_dict.outstanding == 0:

View File

@ -2,16 +2,19 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
from six import iteritems
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt from frappe.utils import flt, formatdate
from frappe.utils import formatdate
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
from six import iteritems
from pprint import pprint
def execute(filters=None): def execute(filters=None):
if not filters: filters = {} if not filters:
filters = {}
columns = get_columns(filters) columns = get_columns(filters)
if filters.get("budget_against_filter"): if filters.get("budget_against_filter"):
@ -43,20 +46,37 @@ def execute(filters=None):
period_data[0] += last_total period_data[0] += last_total
if(filters.get("show_cumulative")): if filters.get("show_cumulative"):
last_total = period_data[0] - period_data[1] last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1] period_data[2] = period_data[0] - period_data[1]
row += period_data row += period_data
totals[2] = totals[0] - totals[1] totals[2] = totals[0] - totals[1]
if filters["period"] != "Yearly" : if filters["period"] != "Yearly":
row += totals row += totals
data.append(row) data.append(row)
return columns, data chart = get_chart_data(filters, columns, data)
return columns, data, None, chart
def get_columns(filters): def get_columns(filters):
columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"] columns = [
{
'label': _(filters.get("budget_against")),
'fieldtype': 'Link',
'fieldname': 'budget_against',
'options': filters.get('budget_against'),
'width': 150
},
{
'label': _('Account'),
'fieldname': 'Account',
'fieldtype': 'Link',
'options': 'Account',
'width': 150
}
]
group_months = False if filters["period"] == "Monthly" else True group_months = False if filters["period"] == "Monthly" else True
@ -65,83 +85,194 @@ def get_columns(filters):
for year in fiscal_year: for year in fiscal_year:
for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
if filters["period"] == "Yearly": if filters["period"] == "Yearly":
labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Variance ") + " " + str(year[0])] labels = [
_("Budget") + " " + str(year[0]),
_("Actual ") + " " + str(year[0]),
_("Variance ") + " " + str(year[0])
]
for label in labels: for label in labels:
columns.append(label+":Float:150") columns.append({
'label': label,
'fieldtype': 'Float',
'fieldname': frappe.scrub(label),
'width': 150
})
else: else:
for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]: for label in [
_("Budget") + " (%s)" + " " + str(year[0]),
_("Actual") + " (%s)" + " " + str(year[0]),
_("Variance") + " (%s)" + " " + str(year[0])
]:
if group_months: if group_months:
label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM")) label = label % (
formatdate(from_date, format_string="MMM")
+ "-"
+ formatdate(to_date, format_string="MMM")
)
else: else:
label = label % formatdate(from_date, format_string="MMM") label = label % formatdate(from_date, format_string="MMM")
columns.append(label+":Float:150") columns.append({
'label': label,
'fieldtype': 'Float',
'fieldname': frappe.scrub(label),
'width': 150
})
if filters["period"] != "Yearly" : if filters["period"] != "Yearly":
return columns + [_("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150", for label in [_("Total Budget"), _("Total Actual"), _("Total Variance")]:
_("Total Variance") + ":Float:150"] columns.append({
'label': label,
'fieldtype': 'Float',
'fieldname': frappe.scrub(label),
'width': 150
})
return columns
else: else:
return columns return columns
def get_cost_centers(filters): def get_cost_centers(filters):
cond = "and 1=1" order_by = ""
if filters.get("budget_against") == "Cost Center": if filters.get("budget_against") == "Cost Center":
cond = "order by lft" order_by = "order by lft"
if filters.get("budget_against") in ["Cost Center", "Project"]: if filters.get("budget_against") in ["Cost Center", "Project"]:
return frappe.db.sql_list("""select name from `tab{tab}` where company=%s return frappe.db.sql_list(
{cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company")) """
select
name
from
`tab{tab}`
where
company = %s
{order_by}
""".format(tab=filters.get("budget_against"), order_by=order_by),
filters.get("company"))
else: else:
return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec return frappe.db.sql_list(
"""
select
name
from
`tab{tab}`
""".format(tab=filters.get("budget_against"))) # nosec
#Get dimension & target details
# Get dimension & target details
def get_dimension_target_details(filters): def get_dimension_target_details(filters):
budget_against = frappe.scrub(filters.get("budget_against"))
cond = "" cond = ""
if filters.get("budget_against_filter"): if filters.get("budget_against_filter"):
cond += " and b.{budget_against} in (%s)".format(budget_against = \ cond += """ and b.{budget_against} in (%s)""".format(
frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter'))) budget_against=budget_against) % ", ".join(["%s"] * len(filters.get("budget_against_filter")))
return frappe.db.sql(""" return frappe.db.sql(
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year """
from `tabBudget` b, `tabBudget Account` ba select
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s b.{budget_against} as budget_against,
and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year b.monthly_distribution,
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond), ba.account,
tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')), ba.budget_amount,
as_dict=True) b.fiscal_year
from
`tabBudget` b,
`tabBudget Account` ba
where
b.name = ba.parent
and b.docstatus = 1
and b.fiscal_year between %s and %s
and b.budget_against = %s
and b.company = %s
{cond}
order by
b.fiscal_year
""".format(
budget_against=budget_against,
cond=cond,
),
tuple(
[
filters.from_fiscal_year,
filters.to_fiscal_year,
filters.budget_against,
filters.company,
]
+ (filters.get("budget_against_filter") or [])
), as_dict=True)
#Get target distribution details of accounts of cost center # Get target distribution details of accounts of cost center
def get_target_distribution_details(filters): def get_target_distribution_details(filters):
target_details = {} target_details = {}
for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation for d in frappe.db.sql(
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md """
where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1): select
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation)) md.name,
mdp.month,
mdp.percentage_allocation
from
`tabMonthly Distribution Percentage` mdp,
`tabMonthly Distribution` md
where
mdp.parent = md.name
and md.fiscal_year between %s and %s
order by
md.fiscal_year
""",
(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
target_details.setdefault(d.name, {}).setdefault(
d.month, flt(d.percentage_allocation)
)
return target_details return target_details
#Get actual details from gl entry # Get actual details from gl entry
def get_actual_details(name, filters): def get_actual_details(name, filters):
cond = "1=1" budget_against = frappe.scrub(filters.get("budget_against"))
budget_against=filters.get("budget_against").replace(" ", "_").lower() cond = ""
if filters.get("budget_against") == "Cost Center": if filters.get("budget_against") == "Cost Center":
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt) cond = """
and lft >= "{lft}"
and rgt <= "{rgt}"
""".format(lft=cc_lft, rgt=cc_rgt)
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year, ac_details = frappe.db.sql(
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against """
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b select
gl.account,
gl.debit,
gl.credit,
gl.fiscal_year,
MONTHNAME(gl.posting_date) as month_name,
b.{budget_against} as budget_against
from
`tabGL Entry` gl,
`tabBudget Account` ba,
`tabBudget` b
where where
b.name = ba.parent b.name = ba.parent
and b.docstatus = 1 and b.docstatus = 1
and ba.account=gl.account and ba.account=gl.account
and b.{budget_against} = gl.{budget_against} and b.{budget_against} = gl.{budget_against}
and gl.fiscal_year between %s and %s and gl.fiscal_year between %s and %s
and b.{budget_against}=%s and b.{budget_against} = %s
and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year and exists(
""".format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year), select
name
from
`tab{tab}`
where
name = gl.{budget_against}
{cond}
)
group by
gl.name
order by gl.fiscal_year
""".format(tab=filters.budget_against, budget_against=budget_against, cond=cond),
(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1) (filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
cc_actual_details = {} cc_actual_details = {}
@ -151,7 +282,6 @@ def get_actual_details(name, filters):
return cc_actual_details return cc_actual_details
def get_dimension_account_month_map(filters): def get_dimension_account_month_map(filters):
import datetime
dimension_target_details = get_dimension_target_details(filters) dimension_target_details = get_dimension_target_details(filters)
tdd = get_target_distribution_details(filters) tdd = get_target_distribution_details(filters)
@ -161,28 +291,89 @@ def get_dimension_account_month_map(filters):
actual_details = get_actual_details(ccd.budget_against, filters) actual_details = get_actual_details(ccd.budget_against, filters)
for month_id in range(1, 13): for month_id in range(1, 13):
month = datetime.date(2013, month_id, 1).strftime('%B') month = datetime.date(2013, month_id, 1).strftime("%B")
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\ cam_map.setdefault(ccd.budget_against, {}).setdefault(
.setdefault(month, frappe._dict({ ccd.account, {}
"target": 0.0, "actual": 0.0 ).setdefault(ccd.fiscal_year, {}).setdefault(
})) month, frappe._dict({"target": 0.0, "actual": 0.0})
)
tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \ month_percentage = (
if ccd.monthly_distribution else 100.0/12 tdd.get(ccd.monthly_distribution, {}).get(month, 0)
if ccd.monthly_distribution
else 100.0 / 12
)
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
for ad in actual_details.get(ccd.account, []): for ad in actual_details.get(ccd.account, []):
if ad.month_name == month: if ad.month_name == month and ad.fiscal_year == ccd.fiscal_year:
tav_dict.actual += flt(ad.debit) - flt(ad.credit) tav_dict.actual += flt(ad.debit) - flt(ad.credit)
return cam_map return cam_map
def get_fiscal_years(filters): def get_fiscal_years(filters):
fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where fiscal_year = frappe.db.sql(
name between %(from_fiscal_year)s and %(to_fiscal_year)s""", """
{'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]}) select
name
from
`tabFiscal Year`
where
name between %(from_fiscal_year)s and %(to_fiscal_year)s
""",
{
"from_fiscal_year": filters["from_fiscal_year"],
"to_fiscal_year": filters["to_fiscal_year"]
})
return fiscal_year return fiscal_year
def get_chart_data(filters, columns, data):
if not data:
return None
labels = []
fiscal_year = get_fiscal_years(filters)
group_months = False if filters["period"] == "Monthly" else True
for year in fiscal_year:
for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
if filters['period'] == 'Yearly':
labels.append(year[0])
else:
if group_months:
label = formatdate(from_date, format_string="MMM") + "-" \
+ formatdate(to_date, format_string="MMM")
labels.append(label)
else:
label = formatdate(from_date, format_string="MMM")
labels.append(label)
no_of_columns = len(labels)
budget_values, actual_values = [0] * no_of_columns, [0] * no_of_columns
for d in data:
values = d[2:]
index = 0
for i in range(no_of_columns):
budget_values[i] += values[index]
actual_values[i] += values[index+1]
index += 3
return {
'data': {
'labels': labels,
'datasets': [
{'name': 'Budget', 'chartType': 'bar', 'values': budget_values},
{'name': 'Actual Expense', 'chartType': 'bar', 'values': actual_values}
]
}
}

View File

@ -84,6 +84,7 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
def get_profit_loss_data(fiscal_year, companies, columns, filters): def get_profit_loss_data(fiscal_year, companies, columns, filters):
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters) income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
company_currency = get_company_currency(filters)
data = [] data = []
data.extend(income or []) data.extend(income or [])
@ -93,7 +94,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters):
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss) chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, True) report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, True)
return data, None, chart, report_summary return data, None, chart, report_summary

View File

@ -19,7 +19,7 @@ from six import itervalues
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False, def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False,
company=None, reset_period_on_fy_change=True): company=None, reset_period_on_fy_change=True, ignore_fiscal_year=False):
"""Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label} """Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label}
Periodicity can be (Yearly, Quarterly, Monthly)""" Periodicity can be (Yearly, Quarterly, Monthly)"""
@ -67,6 +67,7 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
# if a fiscal year ends before a 12 month period # if a fiscal year ends before a 12 month period
period.to_date = year_end_date period.to_date = year_end_date
if not ignore_fiscal_year:
period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0]
period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1]

View File

@ -53,7 +53,7 @@ frappe.query_reports["General Ledger"] = {
"label": __("Voucher No"), "label": __("Voucher No"),
"fieldtype": "Data", "fieldtype": "Data",
on_change: function() { on_change: function() {
frappe.query_report.set_filter_value('group_by', ""); frappe.query_report.set_filter_value('group_by', "Group by Voucher (Consolidated)");
} }
}, },
{ {
@ -154,8 +154,12 @@ frappe.query_reports["General Ledger"] = {
{ {
"fieldname": "include_default_book_entries", "fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"), "label": __("Include Default Book Entries"),
"fieldtype": "Check", "fieldtype": "Check"
"default": 1 },
{
"fieldname": "show_cancelled_entries",
"label": __("Show Cancelled Entries"),
"fieldtype": "Check"
} }
] ]
} }

View File

@ -188,6 +188,9 @@ def get_conditions(filters):
else: else:
conditions.append("finance_book in (%(finance_book)s)") conditions.append("finance_book in (%(finance_book)s)")
if not filters.get("show_cancelled_entries"):
conditions.append("is_cancelled = 0")
from frappe.desk.reportview import build_match_conditions from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("GL Entry") match_conditions = build_match_conditions("GL Entry")
@ -293,6 +296,9 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
if data[key].against_voucher and gle.against_voucher:
data[key].against_voucher += ', ' + gle.against_voucher
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
for gle in gl_entries: for gle in gl_entries:
if (gle.posting_date < from_date or if (gle.posting_date < from_date or

View File

@ -9,8 +9,8 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c
import copy import copy
def execute(filters=None): def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, filters.period_start_date,
filters.periodicity, filters.accumulated_values, filters.company) filters.period_end_date, filters.filter_based_on, filters.periodicity, filters.accumulated_values, filters.company)
columns, data = [], [] columns, data = [], []
@ -35,6 +35,12 @@ def execute(filters=None):
}) })
return columns, data return columns, data
# to avoid error eg: gross_income[0] : list index out of range
if not gross_income:
gross_income = [{}]
if not gross_expense:
gross_expense = [{}]
data.append({ data.append({
"account_name": "'" + _("Included in Gross Profit") + "'", "account_name": "'" + _("Included in Gross Profit") + "'",
"account": "'" + _("Included in Gross Profit") + "'" "account": "'" + _("Included in Gross Profit") + "'"

View File

@ -55,27 +55,27 @@ def get_columns(group_wise_columns, filters):
columns = [] columns = []
column_map = frappe._dict({ column_map = frappe._dict({
"parent": _("Sales Invoice") + ":Link/Sales Invoice:120", "parent": _("Sales Invoice") + ":Link/Sales Invoice:120",
"posting_date": _("Posting Date") + ":Date", "posting_date": _("Posting Date") + ":Date:100",
"posting_time": _("Posting Time"), "posting_time": _("Posting Time") + ":Data:100",
"item_code": _("Item Code") + ":Link/Item", "item_code": _("Item Code") + ":Link/Item:100",
"item_name": _("Item Name"), "item_name": _("Item Name") + ":Data:100",
"item_group": _("Item Group") + ":Link/Item Group", "item_group": _("Item Group") + ":Link/Item Group:100",
"brand": _("Brand"), "brand": _("Brand") + ":Link/Brand:100",
"description": _("Description"), "description": _("Description") +":Data:100",
"warehouse": _("Warehouse") + ":Link/Warehouse", "warehouse": _("Warehouse") + ":Link/Warehouse:100",
"qty": _("Qty") + ":Float", "qty": _("Qty") + ":Float:80",
"base_rate": _("Avg. Selling Rate") + ":Currency/currency", "base_rate": _("Avg. Selling Rate") + ":Currency/currency:100",
"buying_rate": _("Valuation Rate") + ":Currency/currency", "buying_rate": _("Valuation Rate") + ":Currency/currency:100",
"base_amount": _("Selling Amount") + ":Currency/currency", "base_amount": _("Selling Amount") + ":Currency/currency:100",
"buying_amount": _("Buying Amount") + ":Currency/currency", "buying_amount": _("Buying Amount") + ":Currency/currency:100",
"gross_profit": _("Gross Profit") + ":Currency/currency", "gross_profit": _("Gross Profit") + ":Currency/currency:100",
"gross_profit_percent": _("Gross Profit %") + ":Percent", "gross_profit_percent": _("Gross Profit %") + ":Percent:100",
"project": _("Project") + ":Link/Project", "project": _("Project") + ":Link/Project:100",
"sales_person": _("Sales person"), "sales_person": _("Sales person"),
"allocated_amount": _("Allocated Amount") + ":Currency/currency", "allocated_amount": _("Allocated Amount") + ":Currency/currency:100",
"customer": _("Customer") + ":Link/Customer", "customer": _("Customer") + ":Link/Customer:100",
"customer_group": _("Customer Group") + ":Link/Customer Group", "customer_group": _("Customer Group") + ":Link/Customer Group:100",
"territory": _("Territory") + ":Link/Territory" "territory": _("Territory") + ":Link/Territory:100"
}) })
for col in group_wise_columns.get(scrub(filters.group_by)): for col in group_wise_columns.get(scrub(filters.group_by)):
@ -85,7 +85,8 @@ def get_columns(group_wise_columns, filters):
"fieldname": "currency", "fieldname": "currency",
"label" : _("Currency"), "label" : _("Currency"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Currency" "options": "Currency",
"hidden": 1
}) })
return columns return columns
@ -277,7 +278,7 @@ class GrossProfitGenerator(object):
from `tabPurchase Invoice Item` a from `tabPurchase Invoice Item` a
where a.item_code = %s and a.docstatus=1 where a.item_code = %s and a.docstatus=1
and modified <= %s and modified <= %s
order by a.modified desc limit 1""", (item_code,self.filters.to_date)) order by a.modified desc limit 1""", (item_code, self.filters.to_date))
else: else:
last_purchase_rate = frappe.db.sql(""" last_purchase_rate = frappe.db.sql("""
select (a.base_rate / a.conversion_factor) select (a.base_rate / a.conversion_factor)

View File

@ -102,7 +102,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
data.append(row) data.append(row)
if filters.get('group_by'): if filters.get('group_by') and item_list:
total_row = total_row_map.get(prev_group_by_value or d.get('item_name')) total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
data.append(total_row) data.append(total_row)

View File

@ -111,7 +111,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
data.append(row) data.append(row)
if filters.get('group_by'): if filters.get('group_by') and item_list:
total_row = total_row_map.get(prev_group_by_value or d.get('item_name')) total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100) total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
data.append(total_row) data.append(total_row)

View File

@ -1,33 +0,0 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-05-28 15:54:16",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-02-24 20:00:24.302988",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Order Items To Be Billed",
"owner": "Administrator",
"query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order`.`supplier_name` as \"Supplier Name::150\",\n\t`tabPurchase Order Item`.`project` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n\t`tabPurchase Order`.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Closed\"\n and `tabPurchase Order Item`.amount > 0\n\tand (`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) < `tabPurchase Order Item`.base_amount\norder by `tabPurchase Order`.transaction_date asc",
"ref_doctype": "Purchase Invoice",
"report_name": "Purchase Order Items To Be Billed",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Purchase User"
},
{
"role": "Auditor"
},
{
"role": "Accounts Manager"
}
]
}

View File

@ -1,26 +0,0 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
def execute(filters=None):
columns = get_column()
args = get_args()
data = get_ordered_to_be_billed_data(args)
return columns, data
def get_column():
return [
_("Purchase Order") + ":Link/Purchase Order:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Supplier:120", _("Suplier Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
]
def get_args():
return {'doctype': 'Purchase Order', 'party': 'supplier',
'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'}

View File

@ -111,7 +111,7 @@ def get_gle_map(filters):
# {"purchase_invoice": list of dict of all gle created for this invoice} # {"purchase_invoice": list of dict of all gle created for this invoice}
gle_map = {} gle_map = {}
gle = frappe.db.get_all('GL Entry',\ gle = frappe.db.get_all('GL Entry',\
{"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]]}, {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0},
["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"]) ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"])
for d in gle: for d in gle:

View File

@ -46,7 +46,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"default": frappe.defaults.get_user_default("year_end_date"), "default": frappe.defaults.get_user_default("year_end_date"),
}, },
{ {
"fieldname":"cost_center", "fieldname": "cost_center",
"label": __("Cost Center"), "label": __("Cost Center"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Cost Center", "options": "Cost Center",
@ -61,7 +61,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
} }
}, },
{ {
"fieldname":"finance_book", "fieldname": "project",
"label": __("Project"),
"fieldtype": "Link",
"options": "Project"
},
{
"fieldname": "finance_book",
"label": __("Finance Book"), "label": __("Finance Book"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Finance Book", "options": "Finance Book",
@ -97,7 +103,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.dimension_filters.forEach((dimension) => {
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{ frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{
"fieldname": dimension["fieldname"], "fieldname": dimension["fieldname"],
"label": __(dimension["label"]), "label": __(dimension["label"]),
"fieldtype": "Link", "fieldtype": "Link",

View File

@ -69,6 +69,11 @@ def get_data(filters):
gl_entries_by_account = {} gl_entries_by_account = {}
opening_balances = get_opening_balances(filters) opening_balances = get_opening_balances(filters)
#add filter inside list so that the query in financial_statements.py doesn't break
if filters.project:
filters.project = [filters.project]
set_gl_entries_by_account(filters.company, filters.from_date, set_gl_entries_by_account(filters.company, filters.from_date,
filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))
@ -102,6 +107,9 @@ def get_rootwise_opening_balances(filters, report_type):
additional_conditions += """ and cost_center in (select name from `tabCost Center` additional_conditions += """ and cost_center in (select name from `tabCost Center`
where lft >= %s and rgt <= %s)""" % (lft, rgt) where lft >= %s and rgt <= %s)""" % (lft, rgt)
if filters.project:
additional_conditions += " and project = %(project)s"
if filters.finance_book: if filters.finance_book:
fb_conditions = " AND finance_book = %(finance_book)s" fb_conditions = " AND finance_book = %(finance_book)s"
if filters.include_default_book_entries: if filters.include_default_book_entries:
@ -116,6 +124,7 @@ def get_rootwise_opening_balances(filters, report_type):
"from_date": filters.from_date, "from_date": filters.from_date,
"report_type": report_type, "report_type": report_type,
"year_start_date": filters.year_start_date, "year_start_date": filters.year_start_date,
"project": filters.project,
"finance_book": filters.finance_book, "finance_book": filters.finance_book,
"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book') "company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
} }

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