Merge branch 'develop' of https://github.com/frappe/erpnext into loan_patch_and_fixes

This commit is contained in:
Deepesh Garg 2020-07-20 09:53:26 +05:30
commit 04e8af7dca
899 changed files with 23743 additions and 11772 deletions

14
.github/workflows/docker-release.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Trigger Docker build on release
on:
release:
types: [released]
jobs:
curl:
runs-on: ubuntu-latest
container:
image: alpine:latest
steps:
- name: curl
run: |
apk add curl bash
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests

View File

@ -3,17 +3,16 @@
# These owners will be the default owners for everything in # These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence, # the repo. Unless a later match takes precedence,
* @nabinhait manufacturing/ @rohitwaghchaure @marination
manufacturing/ @rohitwaghchaure
accounts/ @deepeshgarg007 @nextchamp-saqib accounts/ @deepeshgarg007 @nextchamp-saqib
loan_management/ @deepeshgarg007 loan_management/ @deepeshgarg007 @rohitwaghchaure
pos* @nextchamp-saqib pos* @nextchamp-saqib @rohitwaghchaure
assets/ @nextchamp-saqib assets/ @nextchamp-saqib @deepeshgarg007
stock/ @marination @rohitwaghchaure stock/ @marination @rohitwaghchaure
buying/ @marination @rohitwaghchaure buying/ @marination @deepeshgarg007
hr/ @Anurag810 hr/ @Anurag810 @rohitwaghchaure
projects/ @hrwX projects/ @hrwX @nextchamp-saqib
support/ @hrwX support/ @hrwX @marination
healthcare/ @ruchamahabal healthcare/ @ruchamahabal @marination
erpnext_integrations/ @Mangesh-Khairnar erpnext_integrations/ @Mangesh-Khairnar @nextchamp-saqib
requirements.txt @gavindsouza requirements.txt @gavindsouza

View File

@ -1,127 +1,284 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from erpnext import get_default_company
import frappe import frappe
import json 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, FiscalYearError
def _get_fiscal_year(date=None):
try:
fiscal_year = get_fiscal_year(date=nowdate(), as_dict=True)
return fiscal_year
except FiscalYearError:
#if no fiscal year for current date then get default fiscal year
try:
fiscal_year = get_fiscal_year(as_dict=True)
return fiscal_year
except FiscalYearError:
#if still no fiscal year found then no accounting data created, return
return None
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(): def get_data():
data = frappe._dict({
"dashboards": [], fiscal_year = _get_fiscal_year(nowdate())
"charts": []
if not fiscal_year:
return frappe._dict()
return frappe._dict({
"dashboards": get_dashboards(),
"charts": get_charts(fiscal_year),
"number_cards": get_number_cards(fiscal_year)
}) })
company = get_company_for_dashboards()
if company:
company_doc = frappe.get_doc("Company", company)
data.dashboards = get_dashboards()
data.charts = get_charts(company_doc)
return data
def get_dashboards(): def get_dashboards():
return [{ return [{
"name": "Accounts", "name": "Accounts",
"dashboard_name": "Accounts", "dashboard_name": "Accounts",
"doctype": "Dashboard",
"charts": [ "charts": [
{ "chart": "Outgoing Bills (Sales Invoice)" }, { "chart": "Profit and Loss" , "width": "Full"},
{ "chart": "Incoming Bills (Purchase Invoice)" }, { "chart": "Incoming Bills (Purchase Invoice)", "width": "Half"},
{ "chart": "Bank Balance" }, { "chart": "Outgoing Bills (Sales Invoice)", "width": "Half"},
{ "chart": "Income" }, { "chart": "Accounts Receivable Ageing", "width": "Half"},
{ "chart": "Expenses" } { "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): def get_charts(fiscal_year):
income_account = company.default_income_account or get_account("Income Account", company.name) company = frappe.get_doc("Company", get_company_for_dashboards())
expense_account = company.default_expense_account or get_account("Expense Account", company.name) bank_account = company.default_bank_account or get_account_name("Bank", company=company.name)
bank_account = company.default_bank_account or get_account("Bank", company.name) default_cost_center = company.cost_center
return [ return [
{ {
"doctype": "Dashboard Chart", "doctype": "Dashboard Charts",
"time_interval": "Quarterly", "name": "Profit and Loss",
"name": "Income",
"chart_name": "Income",
"timespan": "Last Year",
"color": None,
"filters_json": json.dumps({"company": company.name, "account": income_account}),
"source": "Account Balance Timeline",
"chart_type": "Custom",
"timeseries": 1,
"owner": "Administrator", "owner": "Administrator",
"type": "Line" "report_name": "Profit and Loss Statement",
}, "filters_json": json.dumps({
{ "company": company.name,
"doctype": "Dashboard Chart", "filter_based_on": "Fiscal Year",
"time_interval": "Quarterly", "from_fiscal_year": fiscal_year.get('name'),
"name": "Expenses", "to_fiscal_year": fiscal_year.get('name'),
"chart_name": "Expenses", "periodicity": "Monthly",
"timespan": "Last Year", "include_default_book_entries": 1
"color": None, }),
"filters_json": json.dumps({"company": company.name, "account": expense_account}), "type": "Bar",
"source": "Account Balance Timeline", 'timeseries': 0,
"chart_type": "Custom", "chart_type": "Report",
"timeseries": 1, "chart_name": _("Profit and Loss"),
"owner": "Administrator", "is_custom": 1,
"type": "Line" "is_public": 1
},
{
"doctype": "Dashboard Chart",
"time_interval": "Quarterly",
"name": "Bank Balance",
"chart_name": "Bank Balance",
"timespan": "Last Year",
"color": "#ffb868",
"filters_json": json.dumps({"company": company.name, "account": bank_account}),
"source": "Account Balance Timeline",
"chart_type": "Custom",
"timeseries": 1,
"owner": "Administrator",
"type": "Line"
}, },
{ {
"doctype": "Dashboard Chart", "doctype": "Dashboard Chart",
"time_interval": "Monthly", "time_interval": "Monthly",
"name": "Incoming Bills (Purchase Invoice)", "name": "Incoming Bills (Purchase Invoice)",
"chart_name": "Incoming Bills (Purchase Invoice)", "chart_name": _("Incoming Bills (Purchase Invoice)"),
"timespan": "Last Year", "timespan": "Last Year",
"color": "#a83333", "color": "#a83333",
"value_based_on": "base_grand_total", "value_based_on": "base_net_total",
"filters_json": json.dumps({}), "filters_json": json.dumps([["Purchase Invoice", "docstatus", "=", 1]]),
"chart_type": "Sum", "chart_type": "Sum",
"timeseries": 1, "timeseries": 1,
"based_on": "posting_date", "based_on": "posting_date",
"owner": "Administrator", "owner": "Administrator",
"document_type": "Purchase Invoice", "document_type": "Purchase Invoice",
"type": "Bar" "type": "Bar",
"width": "Half",
"is_public": 1
}, },
{ {
"doctype": "Dashboard Chart", "doctype": "Dashboard Chart",
"time_interval": "Monthly",
"name": "Outgoing Bills (Sales Invoice)", "name": "Outgoing Bills (Sales Invoice)",
"chart_name": "Outgoing Bills (Sales Invoice)", "time_interval": "Monthly",
"chart_name": _("Outgoing Bills (Sales Invoice)"),
"timespan": "Last Year", "timespan": "Last Year",
"color": "#7b933d", "color": "#7b933d",
"value_based_on": "base_grand_total", "value_based_on": "base_net_total",
"filters_json": json.dumps({}), "filters_json": json.dumps([["Sales Invoice", "docstatus", "=", 1]]),
"chart_type": "Sum", "chart_type": "Sum",
"timeseries": 1, "timeseries": 1,
"based_on": "posting_date", "based_on": "posting_date",
"owner": "Administrator", "owner": "Administrator",
"document_type": "Sales Invoice", "document_type": "Sales Invoice",
"type": "Bar" "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.get('name'),
"to_fiscal_year": fiscal_year.get('name'),
"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_account(account_type, company): def get_number_cards(fiscal_year):
accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company})
if accounts:
return accounts[0].name
def get_company_for_dashboards(): year_start_date = get_date_str(fiscal_year.get("year_start_date"))
company = get_default_company() year_end_date = get_date_str(fiscal_year.get("year_end_date"))
if not company: return [
company_list = frappe.get_list("Company") {
if company_list: "doctype": "Number Card",
company = company_list[0].name "document_type": "Payment Entry",
return company "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,10 +2,11 @@ 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, cint, get_link_to_form from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, get_first_day, cint, get_link_to_form, rounded
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 from frappe.utils.background_jobs import enqueue
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
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 '''
@ -109,6 +110,18 @@ def get_booking_dates(doc, item, posting_date=None):
order by posting_date desc limit 1 order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
prev_gl_via_je = frappe.db.sql('''
SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c
WHERE p.name = c.parent and p.company=%s and c.account=%s
and c.reference_type=%s and c.reference_name=%s
and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
if prev_gl_via_je:
if (not prev_gl_entry) or (prev_gl_entry and
prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date):
prev_gl_entry = prev_gl_via_je
if prev_gl_entry: if prev_gl_entry:
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
else: else:
@ -130,14 +143,48 @@ def get_booking_dates(doc, item, posting_date=None):
else: else:
return None, None, None return None, None, None
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
if doc.doctype == "Sales Invoice": amount, base_amount = 0, 0
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
deferred_account = "deferred_revenue_account"
else:
total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
deferred_account = "deferred_expense_account"
if not last_gl_entry:
total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \
(item.service_end_date.month - item.service_start_date.month) + 1
prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \
/ flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date)))
actual_months = rounded(total_months * prorate_factor, 1)
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount"))
if base_amount + already_booked_amount > item.base_net_amount:
base_amount = item.base_net_amount - already_booked_amount
if account_currency==doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount/actual_months, item.precision("net_amount"))
if amount + already_booked_amount_in_account_currency > item.net_amount:
amount = item.net_amount - already_booked_amount_in_account_currency
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
partial_month = flt(date_diff(end_date, start_date)) \
/ flt(date_diff(get_last_day(end_date), get_first_day(start_date)))
base_amount = rounded(partial_month, 1) * base_amount
amount = rounded(partial_month, 1) * amount
else:
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
return amount, base_amount
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
amount, base_amount = 0, 0 amount, base_amount = 0, 0
if not last_gl_entry: if not last_gl_entry:
base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount")) base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
@ -146,27 +193,55 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
else: else:
amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount")) amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
else: else:
gl_entries_details = frappe.db.sql(''' already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency: if account_currency==doc.company_currency:
amount = base_amount amount = base_amount
else: else:
already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")) amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
return amount, base_amount return amount, base_amount
def get_already_booked_amount(doc, item):
if doc.doctype == "Sales Invoice":
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
deferred_account = "deferred_revenue_account"
else:
total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
deferred_account = "deferred_expense_account"
gl_entries_details = frappe.db.sql('''
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
journal_entry_details = frappe.db.sql('''
SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
and p.docstatus < 2 group by reference_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0
if doc.currency == doc.company_currency:
already_booked_amount_in_account_currency = already_booked_amount
else:
already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
return already_booked_amount, already_booked_amount_in_account_currency
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_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"
def _book_deferred_revenue_or_expense(item): def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return if not (start_date and end_date): return
@ -181,28 +256,42 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
total_days = date_diff(item.service_end_date, item.service_start_date) + 1 total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(end_date, start_date) + 1 total_booking_days = date_diff(end_date, start_date) + 1
amount, base_amount = calculate_amount(doc, item, last_gl_entry, if book_deferred_entries_based_on == 'Months':
total_days, total_booking_days, account_currency) amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry,
start_date, end_date, total_days, total_booking_days, account_currency)
else:
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
total_days, total_booking_days, account_currency)
make_gl_entries(doc, credit_account, debit_account, against, if via_journal_entry:
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
else:
make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors # 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: if frappe.flags.deferred_accounting_error:
return 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, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry'))
submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries'))
book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on')
for item in doc.get('items'): for item in doc.get('items'):
if item.get(enable_check): if item.get(enable_check):
_book_deferred_revenue_or_expense(item) _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
def process_deferred_accounting(posting_date=today()): def process_deferred_accounting(posting_date=None):
''' Converts deferred income/expense into income/expense ''' Converts deferred income/expense into income/expense
Executed via background jobs on every month end ''' Executed via background jobs on every month end '''
if not posting_date:
posting_date = today()
if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')): if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')):
return return
@ -278,3 +367,83 @@ def send_mail(deferred_process):
and submit manually after resolving errors and submit manually after resolving errors
""").format(get_link_to_form('Process Deferred Accounting', deferred_process)) """).format(get_link_to_form('Process Deferred Accounting', deferred_process))
sendmail_to_system_managers(title, content) sendmail_to_system_managers(title, content)
def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
amount, base_amount, posting_date, project, account_currency, cost_center, item,
deferred_process=None, submit='No'):
if amount == 0: return
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.posting_date = posting_date
journal_entry.company = doc.company
journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \
else 'Deferred Expense'
debit_entry = {
'account': credit_account,
'credit': base_amount,
'credit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
'reference_detail_no': item.name,
'cost_center': cost_center,
'project': project,
}
credit_entry = {
'account': debit_account,
'debit': base_amount,
'debit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
'reference_detail_no': item.name,
'cost_center': cost_center,
'project': project,
}
for dimension in get_accounting_dimensions():
debit_entry.update({
dimension: item.get(dimension)
})
credit_entry.update({
dimension: item.get(dimension)
})
journal_entry.append('accounts', debit_entry)
journal_entry.append('accounts', credit_entry)
try:
journal_entry.save()
if submit:
journal_entry.submit()
except:
frappe.db.rollback()
traceback = frappe.get_traceback()
frappe.log_error(message=traceback)
frappe.flags.deferred_accounting_error = True
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
if doctype == 'Sales Invoice':
credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no},
['income_account', 'deferred_revenue_account'])
else:
credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no},
['deferred_expense_account', 'expense_account'])
if dr_or_cr == 'Debit':
return debit_account
else:
return credit_account

View File

@ -13,12 +13,12 @@
{ {
"hidden": 0, "hidden": 0,
"label": "Accounts Receivable", "label": "Accounts Receivable",
"links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\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 \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Billed\",\n \"name\": \"Ordered Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\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 \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
}, },
{ {
"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,
"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 Clearance Dates\",\n \"name\": \"Bank Clearance\",\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]",
"title": "Banking and Payments"
},
{ {
"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,34 @@
"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-29 12:17:34.844397", "modified": "2020-06-19 12:42:44.054598",
"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": "Journal Entry", "label": "Journal Entry",
"link_to": "Journal Entry", "link_to": "Journal Entry",
@ -136,15 +142,15 @@
"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",
"type": "Report" "type": "Report"
},
{
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
} }
] ]
} }

View File

@ -34,11 +34,15 @@
{ {
"fieldname": "properties", "fieldname": "properties",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"oldfieldtype": "Section Break" "oldfieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "column_break0", "fieldname": "column_break0",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1,
"width": "50%" "width": "50%"
}, },
{ {
@ -49,7 +53,9 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "account_name", "oldfieldname": "account_name",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "account_number", "fieldname": "account_number",
@ -57,13 +63,17 @@
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Account Number", "label": "Account Number",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
"fieldname": "is_group", "fieldname": "is_group",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Group" "label": "Is Group",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "company", "fieldname": "company",
@ -75,7 +85,9 @@
"options": "Company", "options": "Company",
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 1, "remember_last_selected_value": 1,
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "root_type", "fieldname": "root_type",
@ -83,7 +95,9 @@
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Root Type", "label": "Root Type",
"options": "\nAsset\nLiability\nIncome\nExpense\nEquity", "options": "\nAsset\nLiability\nIncome\nExpense\nEquity",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "report_type", "fieldname": "report_type",
@ -91,24 +105,32 @@
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Report Type", "label": "Report Type",
"options": "\nBalance Sheet\nProfit and Loss", "options": "\nBalance Sheet\nProfit and Loss",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"depends_on": "eval:doc.is_group==0", "depends_on": "eval:doc.is_group==0",
"fieldname": "account_currency", "fieldname": "account_currency",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Currency", "label": "Currency",
"options": "Currency" "options": "Currency",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
"fieldname": "inter_company_account", "fieldname": "inter_company_account",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Inter Company Account" "label": "Inter Company Account",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "column_break1", "fieldname": "column_break1",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1,
"width": "50%" "width": "50%"
}, },
{ {
@ -120,7 +142,9 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Account", "options": "Account",
"reqd": 1, "reqd": 1,
"search_index": 1 "search_index": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"description": "Setting Account Type helps in selecting this Account in transactions.", "description": "Setting Account Type helps in selecting this Account in transactions.",
@ -130,7 +154,9 @@
"label": "Account Type", "label": "Account Type",
"oldfieldname": "account_type", "oldfieldname": "account_type",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nTax\nTemporary" "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"description": "Rate at which this tax is applied", "description": "Rate at which this tax is applied",
@ -138,7 +164,9 @@
"fieldtype": "Float", "fieldtype": "Float",
"label": "Rate", "label": "Rate",
"oldfieldname": "tax_rate", "oldfieldname": "tax_rate",
"oldfieldtype": "Currency" "oldfieldtype": "Currency",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"description": "If the account is frozen, entries are allowed to restricted users.", "description": "If the account is frozen, entries are allowed to restricted users.",
@ -147,13 +175,17 @@
"label": "Frozen", "label": "Frozen",
"oldfieldname": "freeze_account", "oldfieldname": "freeze_account",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "No\nYes" "options": "No\nYes",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "balance_must_be", "fieldname": "balance_must_be",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Balance must be", "label": "Balance must be",
"options": "\nDebit\nCredit" "options": "\nDebit\nCredit",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "lft", "fieldname": "lft",
@ -162,7 +194,9 @@
"label": "Lft", "label": "Lft",
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
"search_index": 1 "search_index": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "rgt", "fieldname": "rgt",
@ -171,7 +205,9 @@
"label": "Rgt", "label": "Rgt",
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
"search_index": 1 "search_index": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "old_parent", "fieldname": "old_parent",
@ -179,27 +215,33 @@
"hidden": 1, "hidden": 1,
"label": "Old Parent", "label": "Old Parent",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
"depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)", "depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)",
"fieldname": "include_in_gross", "fieldname": "include_in_gross",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Include in gross" "label": "Include in gross",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
"fieldname": "disabled", "fieldname": "disabled",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Disable" "label": "Disable",
"show_days": 1,
"show_seconds": 1
} }
], ],
"icon": "fa fa-money", "icon": "fa fa-money",
"idx": 1, "idx": 1,
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2020-03-18 17:57:52.063233", "modified": "2020-06-11 15:15:54.338622",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account", "name": "Account",

View File

@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = {
on_change: function() { on_change: function() {
var me = frappe.treeview_settings['Account'].treeview; var me = frappe.treeview_settings['Account'].treeview;
var company = me.page.fields_dict.company.get_value(); var company = me.page.fields_dict.company.get_value();
if (!company) {
frappe.throw(__("Please set a Company"));
}
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.account.account.get_root_company", method: "erpnext.accounts.doctype.account.account.get_root_company",
args: { args: {

View File

@ -72,7 +72,11 @@ def make_dimension_in_accounting_doctypes(doc):
if doctype == "Budget": if doctype == "Budget":
add_dimension_to_budget_doctype(df, doc) add_dimension_to_budget_doctype(df, doc)
else: else:
create_custom_field(doctype, df) meta = frappe.get_meta(doctype, cached=False)
fieldnames = [d.fieldname for d in meta.get("fields")]
if df['fieldname'] not in fieldnames:
create_custom_field(doctype, df)
count += 1 count += 1

View File

@ -19,10 +19,14 @@
"unlink_payment_on_cancellation_of_invoice", "unlink_payment_on_cancellation_of_invoice",
"unlink_advance_payment_on_cancelation_of_order", "unlink_advance_payment_on_cancelation_of_order",
"book_asset_depreciation_entry_automatically", "book_asset_depreciation_entry_automatically",
"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",
"deferred_accounting_settings_section",
"automatically_process_deferred_accounting_entry", "automatically_process_deferred_accounting_entry",
"book_deferred_entries_based_on",
"column_break_18",
"book_deferred_entries_via_journal_entry",
"submit_journal_entries",
"print_settings", "print_settings",
"show_inclusive_tax_in_print", "show_inclusive_tax_in_print",
"column_break_12", "column_break_12",
@ -108,12 +112,6 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Book Asset Depreciation Entry Automatically" "label": "Book Asset Depreciation Entry Automatically"
}, },
{
"default": "0",
"fieldname": "allow_cost_center_in_entry_of_bs_account",
"fieldtype": "Check",
"label": "Allow Cost Center In Entry of Balance Sheet Account"
},
{ {
"default": "1", "default": "1",
"fieldname": "add_taxes_from_item_tax_template", "fieldname": "add_taxes_from_item_tax_template",
@ -189,13 +187,45 @@
"fieldname": "automatically_process_deferred_accounting_entry", "fieldname": "automatically_process_deferred_accounting_entry",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Automatically Process Deferred Accounting Entry" "label": "Automatically Process Deferred Accounting Entry"
},
{
"fieldname": "deferred_accounting_settings_section",
"fieldtype": "Section Break",
"label": "Deferred Accounting Settings"
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "If this is unchecked direct GL Entries will be created to book Deferred Revenue/Expense",
"fieldname": "book_deferred_entries_via_journal_entry",
"fieldtype": "Check",
"label": "Book Deferred Entries Via Journal Entry"
},
{
"default": "0",
"depends_on": "eval:doc.book_deferred_entries_via_journal_entry",
"description": "If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually",
"fieldname": "submit_journal_entries",
"fieldtype": "Check",
"label": "Submit Journal Entries"
},
{
"default": "Days",
"description": "If \"Months\" is selected then fixed amount will be booked as deferred revenue or expense for each month irrespective of number of days in a month. Will be prorated if deferred revenue or expense is not booked for an entire month.",
"fieldname": "book_deferred_entries_based_on",
"fieldtype": "Select",
"label": "Book Deferred Entries Based On",
"options": "Days\nMonths"
} }
], ],
"icon": "icon-cog", "icon": "icon-cog",
"idx": 1, "idx": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2019-12-19 16:58:17.395595", "modified": "2020-06-22 20:13:26.043092",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",

View File

@ -20,7 +20,6 @@ class AccountsSettings(Document):
self.validate_stale_days() self.validate_stale_days()
self.enable_payment_schedule_in_print() self.enable_payment_schedule_in_print()
self.enable_fields_for_cost_center_settings()
def validate_stale_days(self): def validate_stale_days(self):
if not self.allow_stale and cint(self.stale_days) <= 0: if not self.allow_stale and cint(self.stale_days) <= 0:
@ -33,8 +32,3 @@ class AccountsSettings(Document):
for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"): for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check") make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check")
make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check") make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check")
def enable_fields_for_cost_center_settings(self):
show_field = 0 if cint(self.allow_cost_center_in_entry_of_bs_account) else 1
for doctype in ("Sales Invoice", "Purchase Invoice", "Payment Entry"):
make_property_setter(doctype, "cost_center", "hidden", show_field, "Check")

View File

@ -14,7 +14,18 @@ frappe.ui.form.on('Cost Center', {
is_group: 1 is_group: 1
} }
} }
}) });
frm.set_query("cost_center", "distributed_cost_center", function() {
return {
filters: {
company: frm.doc.company,
is_group: 0,
enable_distributed_cost_center: 0,
name: ['!=', frm.doc.name]
}
};
});
}, },
refresh: function(frm) { refresh: function(frm) {
if (!frm.is_new()) { if (!frm.is_new()) {
@ -60,8 +71,13 @@ frappe.ui.form.on('Cost Center', {
"label": "Cost Center Number", "label": "Cost Center Number",
"fieldname": "cost_center_number", "fieldname": "cost_center_number",
"fieldtype": "Data", "fieldtype": "Data",
"reqd": 1,
"default": frm.doc.cost_center_number "default": frm.doc.cost_center_number
},
{
"label": __("Merge with existing"),
"fieldname": "merge",
"fieldtype": "Check",
"default": 0
} }
], ],
primary_action: function() { primary_action: function() {
@ -76,8 +92,9 @@ frappe.ui.form.on('Cost Center', {
args: { args: {
docname: frm.doc.name, docname: frm.doc.name,
cost_center_name: data.cost_center_name, cost_center_name: data.cost_center_name,
cost_center_number: data.cost_center_number, cost_center_number: cstr(data.cost_center_number),
company: frm.doc.company company: frm.doc.company,
merge: data.merge
}, },
callback: function(r) { callback: function(r) {
frappe.dom.unfreeze(); frappe.dom.unfreeze();

View File

@ -16,6 +16,9 @@
"cb0", "cb0",
"is_group", "is_group",
"disabled", "disabled",
"section_break_9",
"enable_distributed_cost_center",
"distributed_cost_center",
"lft", "lft",
"rgt", "rgt",
"old_parent" "old_parent"
@ -119,13 +122,31 @@
"fieldname": "disabled", "fieldname": "disabled",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Disabled" "label": "Disabled"
},
{
"default": "0",
"fieldname": "enable_distributed_cost_center",
"fieldtype": "Check",
"label": "Enable Distributed Cost Center"
},
{
"depends_on": "eval:doc.is_group==0",
"fieldname": "section_break_9",
"fieldtype": "Section Break"
},
{
"depends_on": "enable_distributed_cost_center",
"fieldname": "distributed_cost_center",
"fieldtype": "Table",
"label": "Distributed Cost Center",
"options": "Distributed Cost Center"
} }
], ],
"icon": "fa fa-money", "icon": "fa fa-money",
"idx": 1, "idx": 1,
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2020-04-29 16:09:30.025214", "modified": "2020-06-17 16:09:30.025214",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Cost Center", "name": "Cost Center",

View File

@ -19,6 +19,24 @@ class CostCenter(NestedSet):
def validate(self): def validate(self):
self.validate_mandatory() self.validate_mandatory()
self.validate_parent_cost_center() self.validate_parent_cost_center()
self.validate_distributed_cost_center()
def validate_distributed_cost_center(self):
if cint(self.enable_distributed_cost_center):
if not self.distributed_cost_center:
frappe.throw(_("Please enter distributed cost center"))
if sum(x.percentage_allocation for x in self.distributed_cost_center) != 100:
frappe.throw(_("Total percentage allocation for distributed cost center should be equal to 100"))
if not self.get('__islocal'):
if not cint(frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")) \
and self.check_if_part_of_distributed_cost_center():
frappe.throw(_("Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center"))
if next((True for x in self.distributed_cost_center if x.cost_center == x.parent), False):
frappe.throw(_("Parent Cost Center cannot be added in Distributed Cost Center"))
if check_if_distributed_cost_center_enabled(list(x.cost_center for x in self.distributed_cost_center)):
frappe.throw(_("A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table."))
else:
self.distributed_cost_center = []
def validate_mandatory(self): def validate_mandatory(self):
if self.cost_center_name != self.company and not self.parent_cost_center: if self.cost_center_name != self.company and not self.parent_cost_center:
@ -43,12 +61,15 @@ class CostCenter(NestedSet):
return 1 return 1
def convert_ledger_to_group(self): def convert_ledger_to_group(self):
if cint(self.enable_distributed_cost_center):
frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
if self.check_if_part_of_distributed_cost_center():
frappe.throw(_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group"))
if self.check_gle_exists(): if self.check_gle_exists():
frappe.throw(_("Cost Center with existing transactions can not be converted to group")) frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
else: self.is_group = 1
self.is_group = 1 self.save()
self.save() return 1
return 1
def check_gle_exists(self): def check_gle_exists(self):
return frappe.db.get_value("GL Entry", {"cost_center": self.name}) return frappe.db.get_value("GL Entry", {"cost_center": self.name})
@ -57,6 +78,9 @@ class CostCenter(NestedSet):
return frappe.db.sql("select name from `tabCost Center` where \ return frappe.db.sql("select name from `tabCost Center` where \
parent_cost_center = %s and docstatus != 2", self.name) parent_cost_center = %s and docstatus != 2", self.name)
def check_if_part_of_distributed_cost_center(self):
return frappe.db.get_value("Distributed Cost Center", {"cost_center": self.name})
def before_rename(self, olddn, newdn, merge=False): def before_rename(self, olddn, newdn, merge=False):
# Add company abbr if not provided # Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr from erpnext.setup.doctype.company.company import get_name_with_abbr
@ -100,3 +124,7 @@ def get_name_with_number(new_account, account_number):
if account_number and not new_account[0].isdigit(): if account_number and not new_account[0].isdigit():
new_account = account_number + " - " + new_account new_account = account_number + " - " + new_account
return new_account return new_account
def check_if_distributed_cost_center_enabled(cost_center_list):
value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1)
return next((True for x in value_list if x[0]), False)

View File

@ -22,6 +22,33 @@ class TestCostCenter(unittest.TestCase):
self.assertRaises(frappe.ValidationError, cost_center.save) self.assertRaises(frappe.ValidationError, cost_center.save)
def test_validate_distributed_cost_center(self):
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center - _TC'}):
frappe.get_doc(test_records[0]).insert()
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}):
frappe.get_doc(test_records[1]).insert()
invalid_distributed_cost_center = frappe.get_doc({
"company": "_Test Company",
"cost_center_name": "_Test Distributed Cost Center",
"doctype": "Cost Center",
"is_group": 0,
"parent_cost_center": "_Test Company - _TC",
"enable_distributed_cost_center": 1,
"distributed_cost_center": [{
"cost_center": "_Test Cost Center - _TC",
"percentage_allocation": 40
}, {
"cost_center": "_Test Cost Center 2 - _TC",
"percentage_allocation": 50
}
]
})
self.assertRaises(frappe.ValidationError, invalid_distributed_cost_center.save)
def create_cost_center(**args): def create_cost_center(**args):
args = frappe._dict(args) args = frappe._dict(args)
if args.cost_center_name: if args.cost_center_name:

View File

@ -0,0 +1,40 @@
{
"actions": [],
"creation": "2020-03-19 12:34:01.500390",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"cost_center",
"percentage_allocation"
],
"fields": [
{
"fieldname": "cost_center",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Cost Center",
"options": "Cost Center",
"reqd": 1
},
{
"fieldname": "percentage_allocation",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Percentage Allocation",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-03-19 12:54:43.674655",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Distributed Cost Center",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,10 @@
# -*- 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 DistributedCostCenter(Document):
pass

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe import _ from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate from frappe.utils import flt, fmt_money, getdate, formatdate, cint
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.naming import set_name_from_naming_options from frappe.model.naming import set_name_from_naming_options
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
@ -72,12 +72,6 @@ class GLEntry(Document):
if not self.cost_center and self.voucher_type != 'Period Closing Voucher': if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.") frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.")
.format(self.voucher_type, self.voucher_no, self.account)) .format(self.voucher_type, self.voucher_no, self.account))
else:
from erpnext.accounts.utils import get_allow_cost_center_in_entry_of_bs_account
if not get_allow_cost_center_in_entry_of_bs_account() and self.cost_center:
self.cost_center = None
if self.project:
self.project = None
def validate_dimensions_for_pl_and_bs(self): def validate_dimensions_for_pl_and_bs(self):
@ -134,10 +128,17 @@ class GLEntry(Document):
return self.cost_center_company[self.cost_center] return self.cost_center_company[self.cost_center]
def _check_is_group():
return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group'))
if self.cost_center and _get_cost_center_company() != self.company: if self.cost_center and _get_cost_center_company() != self.company:
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}") frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company)) .format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
if self.cost_center and _check_is_group():
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
def validate_party(self): def validate_party(self):
validate_party_frozen_disabled(self.party_type, self.party) validate_party_frozen_disabled(self.party_type, self.party)

View File

@ -188,14 +188,15 @@ frappe.ui.form.on('Invoice Discounting', {
}, },
show_general_ledger: (frm) => { show_general_ledger: (frm) => {
if(frm.doc.docstatus===1) { if(frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() { cur_frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = { frappe.route_options = {
voucher_no: frm.doc.name, voucher_no: frm.doc.name,
from_date: frm.doc.posting_date, from_date: frm.doc.posting_date,
to_date: frm.doc.posting_date, to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
company: frm.doc.company, company: frm.doc.company,
group_by: "Group by Voucher (Consolidated)" group_by: "Group by Voucher (Consolidated)",
show_cancelled_entries: frm.doc.docstatus === 2
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
}, __("View")); }, __("View"));

View File

@ -3,7 +3,7 @@
# 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, json import frappe, json, erpnext
from frappe import _ 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
@ -134,16 +134,19 @@ class InvoiceDiscounting(AccountsController):
je.append("accounts", { je.append("accounts", {
"account": self.bank_account, "account": self.bank_account,
"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges), "debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
"cost_center": erpnext.get_default_cost_center(self.company)
}) })
je.append("accounts", { je.append("accounts", {
"account": self.bank_charges_account, "account": self.bank_charges_account,
"debit_in_account_currency": flt(self.bank_charges) "debit_in_account_currency": flt(self.bank_charges),
"cost_center": erpnext.get_default_cost_center(self.company)
}) })
je.append("accounts", { je.append("accounts", {
"account": self.short_term_loan, "account": self.short_term_loan,
"credit_in_account_currency": flt(self.total_amount), "credit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting", "reference_type": "Invoice Discounting",
"reference_name": self.name "reference_name": self.name
}) })
@ -151,6 +154,7 @@ class InvoiceDiscounting(AccountsController):
je.append("accounts", { je.append("accounts", {
"account": self.accounts_receivable_discounted, "account": self.accounts_receivable_discounted,
"debit_in_account_currency": flt(d.outstanding_amount), "debit_in_account_currency": flt(d.outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting", "reference_type": "Invoice Discounting",
"reference_name": self.name, "reference_name": self.name,
"party_type": "Customer", "party_type": "Customer",
@ -160,6 +164,7 @@ class InvoiceDiscounting(AccountsController):
je.append("accounts", { je.append("accounts", {
"account": self.accounts_receivable_credit, "account": self.accounts_receivable_credit,
"credit_in_account_currency": flt(d.outstanding_amount), "credit_in_account_currency": flt(d.outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting", "reference_type": "Invoice Discounting",
"reference_name": self.name, "reference_name": self.name,
"party_type": "Customer", "party_type": "Customer",
@ -177,13 +182,15 @@ class InvoiceDiscounting(AccountsController):
je.append("accounts", { je.append("accounts", {
"account": self.short_term_loan, "account": self.short_term_loan,
"debit_in_account_currency": flt(self.total_amount), "debit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting", "reference_type": "Invoice Discounting",
"reference_name": self.name, "reference_name": self.name,
}) })
je.append("accounts", { je.append("accounts", {
"account": self.bank_account, "account": self.bank_account,
"credit_in_account_currency": flt(self.total_amount) "credit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company)
}) })
if getdate(self.loan_end_date) > getdate(nowdate()): if getdate(self.loan_end_date) > getdate(nowdate()):
@ -193,6 +200,7 @@ class InvoiceDiscounting(AccountsController):
je.append("accounts", { je.append("accounts", {
"account": self.accounts_receivable_discounted, "account": self.accounts_receivable_discounted,
"credit_in_account_currency": flt(outstanding_amount), "credit_in_account_currency": flt(outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting", "reference_type": "Invoice Discounting",
"reference_name": self.name, "reference_name": self.name,
"party_type": "Customer", "party_type": "Customer",
@ -202,6 +210,7 @@ class InvoiceDiscounting(AccountsController):
je.append("accounts", { je.append("accounts", {
"account": self.accounts_receivable_unpaid, "account": self.accounts_receivable_unpaid,
"debit_in_account_currency": flt(outstanding_amount), "debit_in_account_currency": flt(outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting", "reference_type": "Invoice Discounting",
"reference_name": self.name, "reference_name": self.name,
"party_type": "Customer", "party_type": "Customer",

View File

@ -6,6 +6,18 @@ frappe.ui.form.on('Item Tax Template', {
frm.set_query("tax_type", "taxes", function(doc) { frm.set_query("tax_type", "taxes", function(doc) {
return { return {
filters: [ filters: [
['Account', 'company', '=', frm.doc.company],
['Account', 'is_group', '=', 0],
['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']]
]
}
});
},
company: function (frm) {
frm.set_query("tax_type", "taxes", function(doc) {
return {
filters: [
['Account', 'company', '=', frm.doc.company],
['Account', 'is_group', '=', 0], ['Account', 'is_group', '=', 0],
['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']]
] ]

View File

@ -1,168 +1,85 @@
{ {
"allow_copy": 0, "allow_import": 1,
"allow_events_in_timeline": 0, "allow_rename": 1,
"allow_guest_to_view": 0, "autoname": "field:title",
"allow_import": 1, "creation": "2018-11-22 22:45:00.370913",
"allow_rename": 1, "doctype": "DocType",
"autoname": "field:title", "document_type": "Setup",
"beta": 0, "editable_grid": 1,
"creation": "2018-11-22 22:45:00.370913", "engine": "InnoDB",
"custom": 0, "field_order": [
"docstatus": 0, "title",
"doctype": "DocType", "company",
"document_type": "Setup", "taxes"
"editable_grid": 1, ],
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "fieldname": "title",
"allow_in_quick_entry": 0, "fieldtype": "Data",
"allow_on_submit": 0, "in_filter": 1,
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Title",
"columns": 0, "no_copy": 1,
"fieldname": "title", "reqd": 1,
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1 "unique": 1
}, },
{ {
"allow_bulk_edit": 0, "fieldname": "taxes",
"allow_in_quick_entry": 0, "fieldtype": "Table",
"allow_on_submit": 0, "label": "Tax Rates",
"bold": 0, "options": "Item Tax Template Detail",
"collapsible": 0, "reqd": 1
"columns": 0, },
"fieldname": "taxes", {
"fieldtype": "Table", "fieldname": "company",
"hidden": 0, "fieldtype": "Link",
"ignore_user_permissions": 0, "label": "Company",
"ignore_xss_filter": 0, "options": "Company",
"in_filter": 0, "reqd": 1
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Tax Rates",
"length": 0,
"no_copy": 0,
"options": "Item Tax Template Detail",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "modified": "2020-06-18 20:27:42.615842",
"hide_heading": 0, "modified_by": "ahmad@havenir.com",
"hide_toolbar": 0, "module": "Accounts",
"idx": 0, "name": "Item Tax Template",
"image_view": 0, "owner": "Administrator",
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-12-21 23:51:16.328340",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Item Tax Template",
"name_case": "",
"owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0, "create": 1,
"cancel": 0, "delete": 1,
"create": 1, "email": 1,
"delete": 1, "export": 1,
"email": 1, "print": 1,
"export": 1, "read": 1,
"if_owner": 0, "report": 1,
"import": 0, "role": "System Manager",
"permlevel": 0, "share": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "create": 1,
"cancel": 0, "delete": 1,
"create": 1, "email": 1,
"delete": 1, "export": 1,
"email": 1, "print": 1,
"export": 1, "read": 1,
"if_owner": 0, "report": 1,
"import": 0, "role": "Accounts Manager",
"permlevel": 0, "share": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "email": 1,
"cancel": 0, "export": 1,
"create": 0, "print": 1,
"delete": 0, "read": 1,
"email": 1, "report": 1,
"export": 1, "role": "Accounts User",
"if_owner": 0, "share": 1
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
} }
], ],
"quick_entry": 0, "show_name_in_global_search": 1,
"read_only": 0, "sort_field": "modified",
"read_only_onload": 0, "sort_order": "DESC",
"search_fields": "", "track_changes": 1
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
} }

View File

@ -2,6 +2,7 @@
{ {
"doctype": "Item Tax Template", "doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 10", "title": "_Test Account Excise Duty @ 10",
"company": "_Test Company",
"taxes": [ "taxes": [
{ {
"doctype": "Item Tax Template Detail", "doctype": "Item Tax Template Detail",
@ -14,6 +15,7 @@
{ {
"doctype": "Item Tax Template", "doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 12", "title": "_Test Account Excise Duty @ 12",
"company": "_Test Company",
"taxes": [ "taxes": [
{ {
"doctype": "Item Tax Template Detail", "doctype": "Item Tax Template Detail",
@ -26,6 +28,7 @@
{ {
"doctype": "Item Tax Template", "doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 15", "title": "_Test Account Excise Duty @ 15",
"company": "_Test Company",
"taxes": [ "taxes": [
{ {
"doctype": "Item Tax Template Detail", "doctype": "Item Tax Template Detail",
@ -38,6 +41,7 @@
{ {
"doctype": "Item Tax Template", "doctype": "Item Tax Template",
"title": "_Test Account Excise Duty @ 20", "title": "_Test Account Excise Duty @ 20",
"company": "_Test Company",
"taxes": [ "taxes": [
{ {
"doctype": "Item Tax Template Detail", "doctype": "Item Tax Template Detail",
@ -50,6 +54,7 @@
{ {
"doctype": "Item Tax Template", "doctype": "Item Tax Template",
"title": "_Test Item Tax Template 1", "title": "_Test Item Tax Template 1",
"company": "_Test Company",
"taxes": [ "taxes": [
{ {
"doctype": "Item Tax Template Detail", "doctype": "Item Tax Template Detail",

View File

@ -13,15 +13,16 @@ frappe.ui.form.on("Journal Entry", {
refresh: function(frm) { refresh: function(frm) {
erpnext.toggle_naming_series(); erpnext.toggle_naming_series();
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,
"from_date": frm.doc.posting_date, "from_date": frm.doc.posting_date,
"to_date": frm.doc.posting_date, "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'),
"company": frm.doc.company, "company": frm.doc.company,
"finance_book": frm.doc.finance_book, "finance_book": frm.doc.finance_book,
"group_by_voucher": 0 "group_by": '',
"show_cancelled_entries": frm.doc.docstatus === 2
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
}, __('View')); }, __('View'));
@ -278,7 +279,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// payroll entry // payroll entry
if(jvd.reference_type==="Payroll Entry") { if(jvd.reference_type==="Payroll Entry") {
return { return {
query: "erpnext.hr.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv", query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv",
}; };
} }

View File

@ -83,7 +83,7 @@
"label": "Entry Type", "label": "Entry Type",
"oldfieldname": "voucher_type", "oldfieldname": "voucher_type",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"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", "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\nDeferred Revenue\nDeferred Expense",
"reqd": 1, "reqd": 1,
"search_index": 1 "search_index": 1
}, },
@ -191,6 +191,7 @@
{ {
"fieldname": "total_debit", "fieldname": "total_debit",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1,
"label": "Total Debit", "label": "Total Debit",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "total_debit", "oldfieldname": "total_debit",
@ -252,7 +253,6 @@
"fieldname": "total_amount", "fieldname": "total_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 1, "hidden": 1,
"in_list_view": 1,
"label": "Total Amount", "label": "Total Amount",
"no_copy": 1, "no_copy": 1,
"options": "total_amount_currency", "options": "total_amount_currency",
@ -503,7 +503,7 @@
"idx": 176, "idx": 176,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-04-29 10:55:28.240916", "modified": "2020-06-02 18:15:46.955697",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
from six import string_types, iteritems from six import string_types, iteritems
@ -54,7 +55,7 @@ class JournalEntry(AccountsController):
def on_cancel(self): def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self) unlink_ref_doc_from_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.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
@ -265,7 +266,10 @@ class JournalEntry(AccountsController):
# set totals # set totals
if not d.reference_name in self.reference_totals: if not d.reference_name in self.reference_totals:
self.reference_totals[d.reference_name] = 0.0 self.reference_totals[d.reference_name] = 0.0
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'):
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
self.reference_types[d.reference_name] = d.reference_type self.reference_types[d.reference_name] = d.reference_type
self.reference_accounts[d.reference_name] = d.account self.reference_accounts[d.reference_name] = d.account
@ -277,10 +281,16 @@ class JournalEntry(AccountsController):
# check if party and account match # check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"): if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if d.reference_type == "Sales Invoice": if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no:
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] debit_or_credit = 'Debit' if d.debit else 'Credit'
party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
debit_or_credit)
else: else:
party_account = against_voucher[1] if d.reference_type == "Sales Invoice":
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
else:
party_account = against_voucher[1]
if (against_voucher[0] != d.party or party_account != d.account): if (against_voucher[0] != d.party or party_account != d.account):
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
@ -513,14 +523,20 @@ class JournalEntry(AccountsController):
"against_voucher_type": d.reference_type, "against_voucher_type": d.reference_type,
"against_voucher": d.reference_name, "against_voucher": d.reference_name,
"remarks": remarks, "remarks": remarks,
"voucher_detail_no": d.reference_detail_no,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"project": d.project, "project": d.project,
"finance_book": self.finance_book "finance_book": self.finance_book
}, item=d) }, item=d)
) )
if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
update_outstanding = 'No'
else:
update_outstanding = 'Yes'
if gl_map: if gl_map:
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj) make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
def get_balance(self): def get_balance(self):
if not self.get('accounts'): if not self.get('accounts'):
@ -824,6 +840,7 @@ def get_opening_accounts(company):
return [{"account": a, "balance": get_balance_on(a)} for a in accounts] return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
@frappe.whitelist()
def get_against_jv(doctype, txt, searchfield, start, page_len, filters): def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail

View File

@ -204,11 +204,8 @@ class TestJournalEntry(unittest.TestCase):
self.assertEqual(jv.inter_company_journal_entry_reference, "") self.assertEqual(jv.inter_company_journal_entry_reference, "")
self.assertEqual(jv1.inter_company_journal_entry_reference, "") self.assertEqual(jv1.inter_company_journal_entry_reference, "")
def test_jv_for_enable_allow_cost_center_in_entry_of_bs_account(self): def test_jv_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
@ -237,15 +234,45 @@ class TestJournalEntry(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 def test_jv_with_project(self):
accounts_settings.save() from erpnext.projects.doctype.project.test_project import make_project
project = make_project({
'project_name': 'Journal Entry Project',
'project_template_name': 'Test Project Template',
'start_date': '2020-01-01'
})
def test_jv_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self): jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False)
for d in jv.accounts:
d.project = project.project_name
jv.voucher_type = "Bank Entry"
jv.multi_currency = 0
jv.cheque_no = "112233"
jv.cheque_date = nowdate()
jv.insert()
jv.submit()
expected_values = {
"_Test Cash - _TC": {
"project": project.project_name
},
"_Test Bank - _TC": {
"project": project.project_name
}
}
gl_entries = frappe.db.sql("""select account, project, debit, credit
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
order by account asc""", jv.name, as_dict=1)
self.assertTrue(gl_entries)
for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["project"], gle.project)
def test_jv_account_and_party_balance_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
@ -261,9 +288,6 @@ class TestJournalEntry(unittest.TestCase):
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center) account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
self.assertEqual(expected_account_balance, account_balance) self.assertEqual(expected_account_balance, account_balance)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save()
def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None): def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None):
if not cost_center: if not cost_center:
cost_center = "_Test Cost Center - _TC" cost_center = "_Test Cost Center - _TC"

View File

@ -18,6 +18,7 @@
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
"project",
"currency_section", "currency_section",
"account_currency", "account_currency",
"column_break_10", "column_break_10",
@ -32,7 +33,7 @@
"reference_type", "reference_type",
"reference_name", "reference_name",
"reference_due_date", "reference_due_date",
"project", "reference_detail_no",
"col_break3", "col_break3",
"is_advance", "is_advance",
"user_remark", "user_remark",
@ -268,12 +269,18 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Bank Account", "label": "Bank Account",
"options": "Bank Account" "options": "Bank Account"
},
{
"fieldname": "reference_detail_no",
"fieldtype": "Data",
"hidden": 1,
"label": "Reference Detail No"
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-04-25 01:47:49.060128", "modified": "2020-06-24 14:06:54.833738",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",

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,19 +39,50 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
}); });
}, },
company: function(frm) { setup_company_filters: function(frm) {
frappe.call({ frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) {
method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account', return {
args: { filters: {
company: frm.doc.company 'company': doc.company
}, }
callback: (r) => { };
if (r.message) { });
frm.doc.__onload.temporary_opening_account = r.message;
frm.trigger('update_invoice_table'); 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) {
if (frm.doc.company) {
frm.trigger('setup_company_filters');
frappe.call({
method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account',
args: {
company: frm.doc.company
},
callback: (r) => {
if (r.message) {
frm.doc.__onload.temporary_opening_account = r.message;
frm.trigger('update_invoice_table');
}
}
})
}
}, },
invoice_type: function(frm) { invoice_type: function(frm) {

View File

@ -68,6 +68,9 @@ class OpeningInvoiceCreationTool(Document):
if not self.company: if not self.company:
frappe.throw(_("Please select the Company")) frappe.throw(_("Please select the Company"))
company_details = frappe.get_cached_value('Company', self.company,
["default_currency", "default_letter_head"], as_dict=1) or {}
for row in self.invoices: for row in self.invoices:
if not row.qty: if not row.qty:
row.qty = 1.0 row.qty = 1.0
@ -99,6 +102,12 @@ class OpeningInvoiceCreationTool(Document):
if not args: if not args:
continue continue
if company_details:
args.update({
"currency": company_details.get("default_currency"),
"letter_head": company_details.get("default_letter_head")
})
doc = frappe.get_doc(args).insert() doc = frappe.get_doc(args).insert()
doc.submit() doc.submit()
names.append(doc.name) names.append(doc.name)
@ -172,8 +181,7 @@ class OpeningInvoiceCreationTool(Document):
"due_date": row.due_date, "due_date": row.due_date,
"posting_date": row.posting_date, "posting_date": row.posting_date,
frappe.scrub(party_type): row.party, frappe.scrub(party_type): row.party,
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice", "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice"
"currency": frappe.get_cached_value('Company', self.company, "default_currency")
}) })
accounting_dimension = get_accounting_dimensions() accounting_dimension = get_accounting_dimensions()

View File

@ -172,8 +172,8 @@ frappe.ui.form.on('Payment Entry', {
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
frm.toggle_display("base_received_amount", ( frm.toggle_display("base_received_amount", (
frm.doc.paid_to_account_currency != company_currency frm.doc.paid_to_account_currency != company_currency
&& frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency
&& frm.doc.base_paid_amount != frm.doc.base_received_amount && frm.doc.base_paid_amount != frm.doc.base_received_amount
)); ));
@ -234,14 +234,15 @@ frappe.ui.form.on('Payment Entry', {
}, },
show_general_ledger: function(frm) { show_general_ledger: 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,
"from_date": frm.doc.posting_date, "from_date": frm.doc.posting_date,
"to_date": frm.doc.posting_date, "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'),
"company": frm.doc.company, "company": frm.doc.company,
group_by: "" "group_by": "",
"show_cancelled_entries": frm.doc.docstatus === 2
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
}, "fa fa-table"); }, "fa fa-table");

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, erpnext, json import frappe, erpnext, json
from frappe import _, scrub, ValidationError from frappe import _, scrub, ValidationError
from frappe.utils import flt, comma_or, nowdate, getdate from frappe.utils import flt, comma_or, nowdate, getdate
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on, get_allow_cost_center_in_entry_of_bs_account from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.setup.utils import get_exchange_rate from erpnext.setup.utils import get_exchange_rate
@ -319,7 +319,7 @@ class PaymentEntry(AccountsController):
invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map.setdefault(key, 0.0)
invoice_payment_amount_map[key] += reference.allocated_amount invoice_payment_amount_map[key] += reference.allocated_amount
if not invoice_paid_amount_map.get(reference.reference_name): if not invoice_paid_amount_map.get(key):
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name}, payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
fields=['paid_amount', 'payment_amount', 'payment_term']) fields=['paid_amount', 'payment_amount', 'payment_term'])
for term in payment_schedule: for term in payment_schedule:
@ -332,12 +332,14 @@ class PaymentEntry(AccountsController):
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
else: else:
outstanding = invoice_paid_amount_map.get(key)['outstanding'] outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
if amount > outstanding: if amount > outstanding:
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s if amount and outstanding:
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
def set_status(self): def set_status(self):
if self.docstatus == 2: if self.docstatus == 2:
@ -451,6 +453,8 @@ 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)]
@ -654,7 +658,7 @@ def get_outstanding_reference_documents(args):
.format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])) .format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]))
# Add cost center condition # Add cost center condition
if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): if args.get("cost_center"):
condition += " and cost_center='%s'" % args.get("cost_center") condition += " and cost_center='%s'" % args.get("cost_center")
date_fields_dict = { date_fields_dict = {
@ -1025,14 +1029,14 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
if bank_amount: if bank_amount:
received_amount = bank_amount received_amount = bank_amount
else: else:
received_amount = paid_amount * doc.conversion_rate received_amount = paid_amount * doc.get('conversion_rate', 1)
else: else:
received_amount = abs(outstanding_amount) received_amount = abs(outstanding_amount)
if bank_amount: if bank_amount:
paid_amount = bank_amount paid_amount = bank_amount
else: else:
# if party account currency and bank currency is different then populate paid amount as well # if party account currency and bank currency is different then populate paid amount as well
paid_amount = received_amount * doc.conversion_rate paid_amount = received_amount * doc.get('conversion_rate', 1)
pe = frappe.new_doc("Payment Entry") pe = frappe.new_doc("Payment Entry")
pe.payment_type = payment_type pe.payment_type = payment_type
@ -1091,17 +1095,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
references = [] references = []
for payment_term in payment_schedule: for payment_term in payment_schedule:
references.append({ payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount,
'reference_doctype': dt,
'reference_name': dn,
'bill_no': doc.get('bill_no'),
'due_date': doc.get('due_date'),
'total_amount': grand_total,
'outstanding_amount': outstanding_amount,
'payment_term': payment_term.payment_term,
'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount,
payment_term.precision('payment_amount')) payment_term.precision('payment_amount'))
})
if payment_term_outstanding:
references.append({
'reference_doctype': dt,
'reference_name': dn,
'bill_no': doc.get('bill_no'),
'due_date': doc.get('due_date'),
'total_amount': grand_total,
'outstanding_amount': outstanding_amount,
'payment_term': payment_term.payment_term,
'allocated_amount': payment_term_outstanding
})
return references return references

View File

@ -460,11 +460,8 @@ class TestPaymentEntry(unittest.TestCase):
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, 0) self.assertEqual(outstanding_amount, 0)
def test_payment_entry_against_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): def test_payment_entry_against_sales_invoice_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
@ -499,39 +496,8 @@ class TestPaymentEntry(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 def test_payment_entry_against_purchase_invoice_with_cost_center(self):
accounts_settings.save()
def test_payment_entry_against_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self):
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save()
si = create_sales_invoice(debit_to="Debtors - _TC")
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
pe.reference_no = "112211-2"
pe.reference_date = nowdate()
pe.paid_to = "_Test Bank - _TC"
pe.paid_amount = si.grand_total
pe.insert()
pe.submit()
gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""", pe.name, as_dict=1)
self.assertTrue(gl_entries)
for gle in gl_entries:
self.assertEqual(gle.cost_center, None)
def test_payment_entry_against_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
@ -566,40 +532,9 @@ class TestPaymentEntry(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 def test_payment_entry_account_and_party_balance_with_cost_center(self):
accounts_settings.save()
def test_payment_entry_against_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self):
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save()
pi = make_purchase_invoice(credit_to="Creditors - _TC")
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "112222-2"
pe.reference_date = nowdate()
pe.paid_from = "_Test Bank - _TC"
pe.paid_amount = pi.grand_total
pe.insert()
pe.submit()
gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""", pe.name, as_dict=1)
self.assertTrue(gl_entries)
for gle in gl_entries:
self.assertEqual(gle.cost_center, None)
def test_payment_entry_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
@ -630,9 +565,6 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(expected_party_balance, party_balance) self.assertEqual(expected_party_balance, party_balance)
self.assertEqual(expected_party_account_balance, party_account_balance) self.assertEqual(expected_party_account_balance, party_account_balance)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save()
def create_payment_terms_template(): def create_payment_terms_template():
create_payment_term('Basic Amount Receivable') create_payment_term('Basic Amount Receivable')
@ -665,4 +597,4 @@ def create_payment_term(name):
frappe.get_doc({ frappe.get_doc({
'doctype': 'Payment Term', 'doctype': 'Payment Term',
'payment_term_name': name 'payment_term_name': name
}).insert() }).insert()

View File

@ -26,6 +26,7 @@ class PaymentOrder(Document):
for d in self.references: for d in self.references:
frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status) frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status)
@frappe.whitelist()
def get_mop_query(doctype, txt, searchfield, start, page_len, filters): def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference` return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference`
where parent = %(parent)s and mode_of_payment like %(txt)s where parent = %(parent)s and mode_of_payment like %(txt)s
@ -36,6 +37,7 @@ def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
'txt': "%%%s%%" % txt 'txt': "%%%s%%" % txt
}) })
@frappe.whitelist()
def get_supplier_query(doctype, txt, searchfield, start, page_len, filters): def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" select supplier from `tabPayment Order Reference` return frappe.db.sql(""" select supplier from `tabPayment Order Reference`
where parent = %(parent)s and supplier like %(txt)s and where parent = %(parent)s and supplier like %(txt)s and
@ -86,4 +88,4 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
je.flags.ignore_mandatory = True je.flags.ignore_mandatory = True
je.save() je.save()
frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name)) frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name))

View File

@ -101,10 +101,10 @@ class PaymentReconciliation(Document):
Having Having
amount > 0 amount > 0
""".format( """.format(
doc=voucher_type, doc=voucher_type,
dr_or_cr=dr_or_cr, dr_or_cr=dr_or_cr,
reconciled_dr_or_cr=reconciled_dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr,
party_type_field=frappe.scrub(self.party_type)), party_type_field=frappe.scrub(self.party_type)),
{ {
'party': self.party, 'party': self.party,
'party_type': self.party_type, 'party_type': self.party_type,
@ -170,7 +170,7 @@ class PaymentReconciliation(Document):
reconcile_against_document(lst) reconcile_against_document(lst)
if dr_or_cr_notes: if dr_or_cr_notes:
reconcile_dr_cr_note(dr_or_cr_notes) reconcile_dr_cr_note(dr_or_cr_notes, self.company)
msgprint(_("Successfully Reconciled")) msgprint(_("Successfully Reconciled"))
self.get_unreconciled_entries() self.get_unreconciled_entries()
@ -261,7 +261,7 @@ class PaymentReconciliation(Document):
return cond return cond
def reconcile_dr_cr_note(dr_cr_notes): def reconcile_dr_cr_note(dr_cr_notes, company):
for d in dr_cr_notes: for d in dr_cr_notes:
voucher_type = ('Credit Note' voucher_type = ('Credit Note'
if d.voucher_type == 'Sales Invoice' else 'Debit Note') if d.voucher_type == 'Sales Invoice' else 'Debit Note')
@ -273,6 +273,7 @@ def reconcile_dr_cr_note(dr_cr_notes):
"doctype": "Journal Entry", "doctype": "Journal Entry",
"voucher_type": voucher_type, "voucher_type": voucher_type,
"posting_date": today(), "posting_date": today(),
"company": company,
"accounts": [ "accounts": [
{ {
'account': d.account, 'account': d.account,
@ -280,7 +281,8 @@ def reconcile_dr_cr_note(dr_cr_notes):
'party_type': d.party_type, 'party_type': d.party_type,
d.dr_or_cr: abs(d.allocated_amount), d.dr_or_cr: abs(d.allocated_amount),
'reference_type': d.against_voucher_type, 'reference_type': d.against_voucher_type,
'reference_name': d.against_voucher 'reference_name': d.against_voucher,
'cost_center': erpnext.get_default_cost_center(company)
}, },
{ {
'account': d.account, 'account': d.account,
@ -289,7 +291,8 @@ def reconcile_dr_cr_note(dr_cr_notes):
reconcile_dr_or_cr: (abs(d.allocated_amount) reconcile_dr_or_cr: (abs(d.allocated_amount)
if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)), if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)),
'reference_type': d.voucher_type, 'reference_type': d.voucher_type,
'reference_name': d.voucher_no 'reference_name': d.voucher_no,
'cost_center': erpnext.get_default_cost_center(company)
} }
] ]
}) })

View File

@ -349,9 +349,10 @@
"read_only": 1 "read_only": 1
} }
], ],
"in_create": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-05-08 10:23:02.815237", "modified": "2020-05-29 17:38:49.392713",
"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") \

View File

@ -25,9 +25,10 @@ frappe.ui.form.on('Period Closing Voucher', {
frappe.route_options = { frappe.route_options = {
"voucher_no": frm.doc.name, "voucher_no": frm.doc.name,
"from_date": frm.doc.posting_date, "from_date": frm.doc.posting_date,
"to_date": frm.doc.posting_date, "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'),
"company": frm.doc.company, "company": frm.doc.company,
group_by_voucher: 0 "group_by": "",
"show_cancelled_entries": frm.doc.docstatus === 2
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
}, "fa fa-table"); }, "fa fa-table");

View File

@ -115,6 +115,7 @@ def get_item_groups(pos_profile):
def get_series(): def get_series():
return frappe.get_meta("Sales Invoice").get_field("naming_series").options or "" return frappe.get_meta("Sales Invoice").get_field("naming_series").options or ""
@frappe.whitelist()
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters): def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
user = frappe.session['user'] user = frappe.session['user']
company = filters.get('company') or frappe.defaults.get_user_default('company') company = filters.get('company') or frappe.defaults.get_user_default('company')

View File

@ -17,6 +17,8 @@ from six import string_types
apply_on_dict = {"Item Code": "items", apply_on_dict = {"Item Code": "items",
"Item Group": "item_groups", "Brand": "brands"} "Item Group": "item_groups", "Brand": "brands"}
other_fields = ["other_item_code", "other_item_group", "other_brand"]
class PricingRule(Document): class PricingRule(Document):
def validate(self): def validate(self):
self.validate_mandatory() self.validate_mandatory()
@ -47,6 +49,13 @@ class PricingRule(Document):
if tocheck and not self.get(tocheck): if tocheck and not self.get(tocheck):
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError) throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
if self.apply_rule_on_other:
o_field = 'other_' + frappe.scrub(self.apply_rule_on_other)
if not self.get(o_field) and o_field in other_fields:
frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory")
.format(frappe.bold(self.apply_rule_on_other)))
if self.price_or_product_discount == 'Price' and not self.rate_or_discount: if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
@ -80,13 +89,27 @@ class PricingRule(Document):
for f in options: for f in options:
if not f: continue if not f: continue
f = frappe.scrub(f) scrubbed_f = frappe.scrub(f)
if f!=fieldname:
self.set(f, None) if logic_field == 'apply_on':
apply_on_f = apply_on_dict.get(f, f)
else:
apply_on_f = scrubbed_f
if scrubbed_f != fieldname:
self.set(apply_on_f, None)
if self.mixed_conditions and self.get("same_item"): if self.mixed_conditions and self.get("same_item"):
self.same_item = 0 self.same_item = 0
apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "")
cleanup_other_fields = (other_fields if not apply_rule_on_other
else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other])
for other_field in cleanup_other_fields:
self.set(other_field, None)
def validate_rate_or_discount(self): def validate_rate_or_discount(self):
for field in ["Rate"]: for field in ["Rate"]:
if flt(self.get(frappe.scrub(field))) < 0: if flt(self.get(frappe.scrub(field))) < 0:
@ -409,6 +432,7 @@ def make_pricing_rule(doctype, docname):
return doc return doc
@frappe.whitelist()
def get_item_uoms(doctype, txt, searchfield, start, page_len, filters): def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
items = [filters.get('value')] items = [filters.get('value')]
if filters.get('apply_on') != 'Item Code': if filters.get('apply_on') != 'Item Code':
@ -419,4 +443,4 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
return frappe.get_all('UOM Conversion Detail', return frappe.get_all('UOM Conversion Detail',
filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))}, filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))},
fields = ["distinct uom"], as_list=1) fields = ["distinct uom"], as_list=1)

View File

@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase):
so.load_from_db() so.load_from_db()
self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item 2") self.assertEqual(so.items[1].item_code, "_Test Item 2")
def test_cumulative_pricing_rule(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Cumulative Pricing Rule",
"apply_on": "Item Code",
"currency": "USD",
"items": [{
"item_code": "_Test Item",
}],
"is_cumulative": 1,
"selling": 1,
"applicable_for": "Customer",
"customer": "_Test Customer",
"rate_or_discount": "Discount Percentage",
"rate": 0,
"min_amt": 0,
"max_amt": 10000,
"discount_percentage": 17.5,
"price_or_product_discount": "Price",
"company": "_Test Company",
"valid_from": frappe.utils.nowdate(),
"valid_upto": frappe.utils.nowdate()
}
frappe.get_doc(test_record.copy()).insert()
args = frappe._dict({
"item_code": "_Test Item",
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
"name": None,
"transaction_date": frappe.utils.nowdate()
})
details = get_item_details(args)
self.assertTrue(details)
def make_pricing_rule(**args): def make_pricing_rule(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -319,7 +319,9 @@ def apply_internal_priority(pricing_rules, field_set, args):
filtered_rules = [] filtered_rules = []
for field in field_set: for field in field_set:
if args.get(field): if args.get(field):
filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules) # filter function always returns a filter object even if empty
# list conversion is necessary to check for an empty result
filtered_rules = list(filter(lambda x: x.get(field)==args.get(field), pricing_rules))
if filtered_rules: break if filtered_rules: break
return filtered_rules or pricing_rules return filtered_rules or pricing_rules
@ -366,8 +368,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
sum_qty, sum_amt = [0, 0] sum_qty, sum_amt = [0, 0]
doctype = doc.get('parenttype') or doc.doctype doctype = doc.get('parenttype') or doc.doctype
date_field = ('transaction_date' date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date'
if doc.get('transaction_date') else 'posting_date')
child_doctype = '{0} Item'.format(doctype) child_doctype = '{0} Item'.format(doctype)
apply_on = frappe.scrub(pr_doc.get('apply_on')) apply_on = frappe.scrub(pr_doc.get('apply_on'))

View File

@ -10,6 +10,18 @@ frappe.ui.form.on('Process Deferred Accounting', {
} }
}; };
}); });
if (frm.doc.company) {
frm.set_query("account", function() {
return {
filters: {
'company': frm.doc.company,
'root_type': 'Liability',
'is_group': 0
}
};
});
}
}, },
validate: function() { validate: function() {

View File

@ -238,6 +238,12 @@ class PurchaseInvoice(BuyingController):
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")): not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock and (not item.from_warehouse): if self.update_stock and (not item.from_warehouse):
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2}
is not linked to warehouse {3} or it is not the default inventory account'''.format(
item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]),
frappe.bold(item.expense_account), frappe.bold(item.warehouse))))
item.expense_account = warehouse_account[item.warehouse]["account"] item.expense_account = warehouse_account[item.warehouse]["account"]
else: else:
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not # check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
@ -247,10 +253,21 @@ class PurchaseInvoice(BuyingController):
(item.purchase_receipt, stock_not_billed_account)) (item.purchase_receipt, stock_not_billed_account))
if negative_expense_booked_in_pr: if negative_expense_booked_in_pr:
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because
expense is booked against this account in Purchase Receipt {2}'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))))
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
else: else:
# If no purchase receipt present then book expense in 'Stock Received But Not Billed' # If no purchase receipt present then book expense in 'Stock Received But Not Billed'
# This is done in cases when Purchase Invoice is created before Purchase Receipt # This is done in cases when Purchase Invoice is created before Purchase Receipt
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase
Receipt is created against Item {2}. This is done to handle accounting for cases
when Purchase Receipt is created after Purchase Invoice'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))))
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
@ -421,6 +438,8 @@ class PurchaseInvoice(BuyingController):
self.make_tax_gl_entries(gl_entries) self.make_tax_gl_entries(gl_entries)
gl_entries = make_regional_gl_entries(gl_entries, self)
gl_entries = merge_similar_entries(gl_entries) gl_entries = merge_similar_entries(gl_entries)
self.make_payment_gl_entries(gl_entries) self.make_payment_gl_entries(gl_entries)
@ -459,6 +478,7 @@ class PurchaseInvoice(BuyingController):
if self.party_account_currency==self.company_currency else grand_total, if self.party_account_currency==self.company_currency else grand_total,
"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,
"project": self.project,
"cost_center": self.cost_center "cost_center": self.cost_center
}, self.party_account_currency, item=self) }, self.party_account_currency, item=self)
) )
@ -499,6 +519,7 @@ class PurchaseInvoice(BuyingController):
"account": warehouse_account[item.warehouse]['account'], "account": warehouse_account[item.warehouse]['account'],
"against": warehouse_account[item.from_warehouse]["account"], "against": warehouse_account[item.from_warehouse]["account"],
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": warehouse_debit_amount, "debit": warehouse_debit_amount,
}, warehouse_account[item.warehouse]["account_currency"], item=item)) }, warehouse_account[item.warehouse]["account_currency"], item=item))
@ -508,6 +529,7 @@ class PurchaseInvoice(BuyingController):
"account": warehouse_account[item.from_warehouse]['account'], "account": warehouse_account[item.from_warehouse]['account'],
"against": warehouse_account[item.warehouse]["account"], "against": warehouse_account[item.warehouse]["account"],
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")), "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
}, warehouse_account[item.from_warehouse]["account_currency"], item=item)) }, warehouse_account[item.from_warehouse]["account_currency"], item=item))
@ -531,7 +553,7 @@ class PurchaseInvoice(BuyingController):
"debit": warehouse_debit_amount, "debit": warehouse_debit_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project "project": item.project or self.project
}, account_currency, item=item) }, account_currency, item=item)
) )
@ -544,7 +566,7 @@ class PurchaseInvoice(BuyingController):
"cost_center": item.cost_center, "cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(amount), "credit": flt(amount),
"project": item.project "project": item.project or self.project
}, item=item)) }, item=item))
# sub-contracting warehouse # sub-contracting warehouse
@ -557,6 +579,7 @@ class PurchaseInvoice(BuyingController):
"account": supplier_warehouse_account, "account": supplier_warehouse_account,
"against": item.expense_account, "against": item.expense_account,
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.rm_supp_cost) "credit": flt(item.rm_supp_cost)
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) }, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
@ -570,12 +593,26 @@ class PurchaseInvoice(BuyingController):
else: else:
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items'))
if auto_accounting_for_non_stock_items:
service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
if item.purchase_receipt:
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0,
'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail,
'account':service_received_but_not_billed_account}, ['name'])
if expense_booked_in_pr:
expense_account = service_received_but_not_billed_account
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": expense_account, "account": expense_account,
"against": self.supplier, "against": self.supplier,
"debit": amount, "debit": amount,
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project "project": item.project or self.project
}, account_currency, item=item)) }, account_currency, item=item))
# If asset is bought through this document and not linked to PR # If asset is bought through this document and not linked to PR
@ -588,7 +625,7 @@ class PurchaseInvoice(BuyingController):
"cost_center": item.cost_center, "cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.landed_cost_voucher_amount), "credit": flt(item.landed_cost_voucher_amount),
"project": item.project "project": item.project or self.project
}, item=item)) }, item=item))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
@ -597,7 +634,7 @@ class PurchaseInvoice(BuyingController):
"cost_center": item.cost_center, "cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": flt(item.landed_cost_voucher_amount), "debit": flt(item.landed_cost_voucher_amount),
"project": item.project "project": item.project or self.project
}, item=item)) }, item=item))
# update gross amount of asset bought through this document # update gross amount of asset bought through this document
@ -623,7 +660,8 @@ class PurchaseInvoice(BuyingController):
"against": self.supplier, "against": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")), "debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
"remarks": self.remarks or "Accounting Entry for Stock", "remarks": self.remarks or "Accounting Entry for Stock",
"cost_center": self.cost_center "cost_center": self.cost_center,
"project": item.project or self.project
}, item=item) }, item=item)
) )
@ -652,7 +690,8 @@ class PurchaseInvoice(BuyingController):
"debit": base_asset_amount, "debit": base_asset_amount,
"debit_in_account_currency": (base_asset_amount "debit_in_account_currency": (base_asset_amount
if arbnb_currency == self.company_currency else asset_amount), if arbnb_currency == self.company_currency else asset_amount),
"cost_center": item.cost_center "cost_center": item.cost_center,
"project": item.project or self.project
}, item=item)) }, item=item))
if item.item_tax_amount: if item.item_tax_amount:
@ -662,6 +701,7 @@ class PurchaseInvoice(BuyingController):
"against": self.supplier, "against": self.supplier,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"), "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project or self.project,
"credit": item.item_tax_amount, "credit": item.item_tax_amount,
"credit_in_account_currency": (item.item_tax_amount "credit_in_account_currency": (item.item_tax_amount
if asset_eiiav_currency == self.company_currency else if asset_eiiav_currency == self.company_currency else
@ -678,7 +718,8 @@ class PurchaseInvoice(BuyingController):
"debit": base_asset_amount, "debit": base_asset_amount,
"debit_in_account_currency": (base_asset_amount "debit_in_account_currency": (base_asset_amount
if cwip_account_currency == self.company_currency else asset_amount), if cwip_account_currency == self.company_currency else asset_amount),
"cost_center": self.cost_center "cost_center": self.cost_center,
"project": item.project or self.project
}, item=item)) }, item=item))
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
@ -689,6 +730,7 @@ class PurchaseInvoice(BuyingController):
"remarks": self.get("remarks") or _("Accounting Entry for Asset"), "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"cost_center": item.cost_center, "cost_center": item.cost_center,
"credit": item.item_tax_amount, "credit": item.item_tax_amount,
"project": item.project or self.project,
"credit_in_account_currency": (item.item_tax_amount "credit_in_account_currency": (item.item_tax_amount
if asset_eiiav_currency == self.company_currency else if asset_eiiav_currency == self.company_currency else
item.item_tax_amount / self.conversion_rate) item.item_tax_amount / self.conversion_rate)
@ -704,7 +746,7 @@ class PurchaseInvoice(BuyingController):
"cost_center": item.cost_center, "cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.landed_cost_voucher_amount), "credit": flt(item.landed_cost_voucher_amount),
"project": item.project "project": item.project or self.project
}, item=item)) }, item=item))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
@ -713,7 +755,7 @@ class PurchaseInvoice(BuyingController):
"cost_center": item.cost_center, "cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": flt(item.landed_cost_voucher_amount), "debit": flt(item.landed_cost_voucher_amount),
"project": item.project "project": item.project or self.project
}, item=item)) }, item=item))
# update gross amount of assets bought through this document # update gross amount of assets bought through this document
@ -748,7 +790,7 @@ class PurchaseInvoice(BuyingController):
"debit": stock_adjustment_amt, "debit": stock_adjustment_amt,
"remarks": self.get("remarks") or _("Stock Adjustment"), "remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project "project": item.project or self.project
}, account_currency, item=item) }, account_currency, item=item)
) )
@ -840,7 +882,8 @@ class PurchaseInvoice(BuyingController):
if self.party_account_currency==self.company_currency else self.paid_amount, if self.party_account_currency==self.company_currency else self.paid_amount,
"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,
"project": self.project
}, self.party_account_currency, item=self) }, self.party_account_currency, item=self)
) )
@ -872,7 +915,8 @@ class PurchaseInvoice(BuyingController):
if self.party_account_currency==self.company_currency else self.write_off_amount, if self.party_account_currency==self.company_currency else self.write_off_amount,
"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,
"project": self.project
}, self.party_account_currency, item=self) }, self.party_account_currency, item=self)
) )
gl_entries.append( gl_entries.append(
@ -1020,7 +1064,7 @@ 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): def set_status(self, update=False, status=None, update_modified=True):
if self.is_new(): if self.is_new():
if self.get('amended_from'): if self.get('amended_from'):
@ -1066,6 +1110,10 @@ def get_list_context(context=None):
}) })
return list_context return list_context
@erpnext.allow_regional
def make_regional_gl_entries(gl_entries, doc):
return gl_entries
@frappe.whitelist() @frappe.whitelist()
def make_debit_note(source_name, target_doc=None): def make_debit_note(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc from erpnext.controllers.sales_and_purchase_return import make_return_doc

View File

@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = {
} else if(frappe.datetime.get_diff(doc.due_date) < 0) { } else if(frappe.datetime.get_diff(doc.due_date) < 0) {
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"]; return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
} else { } else {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"]; return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
} }
} else if(cint(doc.is_return)) { } else if(cint(doc.is_return)) {
return [__("Return"), "darkgrey", "is_return,=,Yes"]; return [__("Return"), "darkgrey", "is_return,=,Yes"];
@ -24,4 +24,4 @@ frappe.listview_settings['Purchase Invoice'] = {
return [__("Paid"), "green", "outstanding_amount,=,0"]; return [__("Paid"), "green", "outstanding_amount,=,0"];
} }
} }
}; };

View File

@ -7,14 +7,16 @@ import unittest
import frappe, erpnext import frappe, erpnext
import frappe.model import frappe.model
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from frappe.utils import cint, flt, today, nowdate, add_days from frappe.utils import cint, flt, today, nowdate, add_days, getdate
import frappe.defaults import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records, make_purchase_receipt, get_taxes test_records as pr_test_records, make_purchase_receipt, get_taxes
from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.exceptions import InvalidCurrency from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.projects.doctype.project.test_project import make_project
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
from erpnext.stock.doctype.item.test_item import create_item
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"] test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
test_ignore = ["Serial No"] test_ignore = ["Serial No"]
@ -434,6 +436,8 @@ class TestPurchaseInvoice(unittest.TestCase):
) )
def test_total_purchase_cost_for_project(self): def test_total_purchase_cost_for_project(self):
make_project({'project_name':'_Test Project'})
existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount) existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount)
from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""") from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""")
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0 existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
@ -807,11 +811,8 @@ class TestPurchaseInvoice(unittest.TestCase):
pi_doc = frappe.get_doc('Purchase Invoice', pi.name) pi_doc = frappe.get_doc('Purchase Invoice', pi.name)
self.assertEqual(pi_doc.outstanding_amount, 0) self.assertEqual(pi_doc.outstanding_amount, 0)
def test_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): def test_purchase_invoice_with_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
@ -837,13 +838,7 @@ class TestPurchaseInvoice(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 def test_purchase_invoice_without_cost_center(self):
accounts_settings.save()
def test_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self):
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save()
cost_center = "_Test Cost Center - _TC" cost_center = "_Test Cost Center - _TC"
pi = make_purchase_invoice(credit_to="Creditors - _TC") pi = make_purchase_invoice(credit_to="Creditors - _TC")
@ -866,6 +861,104 @@ class TestPurchaseInvoice(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
def test_purchase_invoice_with_project_link(self):
project = make_project({
'project_name': 'Purchase Invoice Project',
'project_template_name': 'Test Project Template',
'start_date': '2020-01-01'
})
item_project = make_project({
'project_name': 'Purchase Invoice Item Project',
'project_template_name': 'Test Project Template',
'start_date': '2019-06-01'
})
pi = make_purchase_invoice(credit_to="Creditors - _TC" ,do_not_save=1)
pi.items[0].project = item_project.project_name
pi.project = project.project_name
pi.submit()
expected_values = {
"Creditors - _TC": {
"project": project.project_name
},
"_Test Account Cost for Goods Sold - _TC": {
"project": item_project.project_name
}
}
gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""", pi.name, as_dict=1)
self.assertTrue(gl_entries)
for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["project"], gle.project)
def test_deferred_expense_via_journal_entry(self):
deferred_account = create_account(account_name="Deferred Expense",
parent_account="Current Assets - _TC", company="_Test Company")
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.book_deferred_entries_via_journal_entry = 1
acc_settings.submit_journal_entries = 1
acc_settings.save()
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_expense = 1
item.deferred_expense_account = deferred_account
item.save()
pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True)
pi.set_posting_time = 1
pi.posting_date = '2019-03-15'
pi.items[0].enable_deferred_expense = 1
pi.items[0].service_start_date = "2019-01-10"
pi.items[0].service_end_date = "2019-03-15"
pi.items[0].deferred_expense_account = deferred_account
pi.save()
pi.submit()
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
start_date="2019-01-01",
end_date="2019-03-31",
type="Expense",
company="_Test Company"
))
pda1.insert()
pda1.submit()
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 0.0, 33.85, "2019-01-31"],
[deferred_account, 33.85, 0.0, "2019-01-31"],
["_Test Account Cost for Goods Sold - _TC", 0.0, 43.08, "2019-02-28"],
[deferred_account, 43.08, 0.0, "2019-02-28"],
["_Test Account Cost for Goods Sold - _TC", 0.0, 23.07, "2019-03-15"],
[deferred_account, 23.07, 0.0, "2019-03-15"]
]
gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
order by posting_date asc, account asc""", (pi.items[0].name, pi.posting_date), as_dict=1)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.credit)
self.assertEqual(expected_gle[i][2], gle.debit)
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.book_deferred_entries_via_journal_entry = 0
acc_settings.submit_journal_entriessubmit_journal_entries = 0
acc_settings.save()
def unlink_payment_on_cancel_of_invoice(enable=1): def unlink_payment_on_cancel_of_invoice(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings") accounts_settings = frappe.get_doc("Accounts Settings")

File diff suppressed because it is too large Load Diff

View File

@ -582,14 +582,14 @@ class SalesInvoice(SellingController):
def validate_item_code(self): def validate_item_code(self):
for d in self.get('items'): for d in self.get('items'):
if not d.item_code: if not d.item_code and self.is_opening == "No":
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
def validate_warehouse(self): def validate_warehouse(self):
super(SalesInvoice, self).validate_warehouse() super(SalesInvoice, self).validate_warehouse()
for d in self.get_item_list(): for d in self.get_item_list():
if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
def validate_delivery_note(self): def validate_delivery_note(self):
@ -790,7 +790,8 @@ class SalesInvoice(SellingController):
if self.party_account_currency==self.company_currency else grand_total, if self.party_account_currency==self.company_currency else grand_total,
"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,
"project": self.project
}, self.party_account_currency, item=self) }, self.party_account_currency, item=self)
) )
@ -845,7 +846,8 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
if account_currency==self.company_currency if account_currency==self.company_currency
else flt(item.net_amount, item.precision("net_amount"))), else flt(item.net_amount, item.precision("net_amount"))),
"cost_center": item.cost_center "cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item) }, account_currency, item=item)
) )
@ -926,7 +928,8 @@ class SalesInvoice(SellingController):
if self.party_account_currency==self.company_currency else flt(self.change_amount), if self.party_account_currency==self.company_currency else flt(self.change_amount),
"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,
"project": self.project
}, self.party_account_currency, item=self) }, self.party_account_currency, item=self)
) )
@ -959,7 +962,8 @@ class SalesInvoice(SellingController):
else flt(self.write_off_amount, self.precision("write_off_amount"))), else flt(self.write_off_amount, self.precision("write_off_amount"))),
"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,
"project": self.project
}, self.party_account_currency, item=self) }, self.party_account_currency, item=self)
) )
gl_entries.append( gl_entries.append(
@ -1109,7 +1113,10 @@ class SalesInvoice(SellingController):
expiry_date=self.posting_date, include_expired_entry=True) expiry_date=self.posting_date, include_expired_entry=True)
if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \ if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)): (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
points_earned = cint(eligible_amount/lp_details.collection_factor)
collection_factor = lp_details.collection_factor if lp_details.collection_factor else 1.0
points_earned = cint(eligible_amount/collection_factor)
doc = frappe.get_doc({ doc = frappe.get_doc({
"doctype": "Loyalty Point Entry", "doctype": "Loyalty Point Entry",
"company": self.company, "company": self.company,
@ -1450,11 +1457,17 @@ def get_inter_company_details(doc, doctype):
parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company}) parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company})
company = frappe.get_cached_value("Customer", doc.customer, "represents_company") company = frappe.get_cached_value("Customer", doc.customer, "represents_company")
if not parties:
frappe.throw(_('No Supplier found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
party = get_internal_party(parties, "Supplier", doc) party = get_internal_party(parties, "Supplier", doc)
else: else:
parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company}) parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company})
company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company") company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company")
if not parties:
frappe.throw(_('No Customer found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
party = get_internal_party(parties, "Customer", doc) party = get_internal_party(parties, "Customer", doc)
return { return {
@ -1519,14 +1532,22 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def update_details(source_doc, target_doc, source_parent): def update_details(source_doc, target_doc, source_parent):
target_doc.inter_company_invoice_reference = source_doc.name target_doc.inter_company_invoice_reference = source_doc.name
if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]: if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]:
currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.supplier = details.get("party") target_doc.supplier = details.get("party")
target_doc.buying_price_list = source_doc.selling_price_list target_doc.buying_price_list = source_doc.selling_price_list
if currency:
target_doc.currency = currency
else: else:
currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.customer = details.get("party") target_doc.customer = details.get("party")
target_doc.selling_price_list = source_doc.buying_price_list target_doc.selling_price_list = source_doc.buying_price_list
if currency:
target_doc.currency = currency
doclist = get_mapped_doc(doctype, source_name, { doclist = get_mapped_doc(doctype, source_name, {
doctype: { doctype: {
"doctype": target_doctype, "doctype": target_doctype,

View File

@ -3,6 +3,7 @@
"company": "_Test Company", "company": "_Test Company",
"conversion_rate": 1.0, "conversion_rate": 1.0,
"currency": "INR", "currency": "INR",
"cost_center": "_Test Cost Center - _TC",
"customer": "_Test Customer", "customer": "_Test Customer",
"customer_name": "_Test Customer", "customer_name": "_Test Customer",
"debit_to": "_Test Receivable - _TC", "debit_to": "_Test Receivable - _TC",
@ -37,7 +38,8 @@
"charge_type": "On Net Total", "charge_type": "On Net Total",
"description": "VAT", "description": "VAT",
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",
"parentfield": "taxes", "parentfield": "taxes",
"cost_center": "_Test Cost Center - _TC",
"rate": 6 "rate": 6
}, },
{ {
@ -45,7 +47,8 @@
"charge_type": "On Net Total", "charge_type": "On Net Total",
"description": "Service Tax", "description": "Service Tax",
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",
"parentfield": "taxes", "parentfield": "taxes",
"cost_center": "_Test Cost Center - _TC",
"rate": 6.36 "rate": 6.36
} }
], ],
@ -76,6 +79,7 @@
"customer_name": "_Test Customer", "customer_name": "_Test Customer",
"debit_to": "_Test Receivable - _TC", "debit_to": "_Test Receivable - _TC",
"doctype": "Sales Invoice", "doctype": "Sales Invoice",
"cost_center": "_Test Cost Center - _TC",
"items": [ "items": [
{ {
"amount": 500.0, "amount": 500.0,
@ -107,7 +111,8 @@
"charge_type": "On Net Total", "charge_type": "On Net Total",
"description": "VAT", "description": "VAT",
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",
"parentfield": "taxes", "parentfield": "taxes",
"cost_center": "_Test Cost Center - _TC",
"rate": 16 "rate": 16
}, },
{ {
@ -115,7 +120,8 @@
"charge_type": "On Net Total", "charge_type": "On Net Total",
"description": "Service Tax", "description": "Service Tax",
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",
"parentfield": "taxes", "parentfield": "taxes",
"cost_center": "_Test Cost Center - _TC",
"rate": 10 "rate": 10
} }
], ],
@ -132,6 +138,7 @@
"customer_name": "_Test Customer", "customer_name": "_Test Customer",
"debit_to": "_Test Receivable - _TC", "debit_to": "_Test Receivable - _TC",
"doctype": "Sales Invoice", "doctype": "Sales Invoice",
"cost_center": "_Test Cost Center - _TC",
"items": [ "items": [
{ {
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
@ -259,6 +266,7 @@
"customer_name": "_Test Customer", "customer_name": "_Test Customer",
"debit_to": "_Test Receivable - _TC", "debit_to": "_Test Receivable - _TC",
"doctype": "Sales Invoice", "doctype": "Sales Invoice",
"cost_center": "_Test Cost Center - _TC",
"items": [ "items": [
{ {
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",

View File

@ -1640,11 +1640,8 @@ class TestSalesInvoice(unittest.TestCase):
si_doc = frappe.get_doc('Sales Invoice', si.name) si_doc = frappe.get_doc('Sales Invoice', si.name)
self.assertEqual(si_doc.outstanding_amount, 0) self.assertEqual(si_doc.outstanding_amount, 0)
def test_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): def test_sales_invoice_with_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
@ -1669,14 +1666,47 @@ class TestSalesInvoice(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
def test_sales_invoice_with_project_link(self):
from erpnext.projects.doctype.project.test_project import make_project
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 project = make_project({
accounts_settings.save() 'project_name': 'Sales Invoice Project',
'project_template_name': 'Test Project Template',
'start_date': '2020-01-01'
})
item_project = make_project({
'project_name': 'Sales Invoice Item Project',
'project_template_name': 'Test Project Template',
'start_date': '2019-06-01'
})
def test_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): sales_invoice = create_sales_invoice(do_not_save=1)
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') sales_invoice.items[0].project = item_project.project_name
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 sales_invoice.project = project.project_name
accounts_settings.save()
sales_invoice.submit()
expected_values = {
"Debtors - _TC": {
"project": project.project_name
},
"Sales - _TC": {
"project": item_project.project_name
}
}
gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""", sales_invoice.name, as_dict=1)
self.assertTrue(gl_entries)
for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["project"], gle.project)
def test_sales_invoice_without_cost_center(self):
cost_center = "_Test Cost Center - _TC" cost_center = "_Test Cost Center - _TC"
si = create_sales_invoice(debit_to="Debtors - _TC") si = create_sales_invoice(debit_to="Debtors - _TC")
@ -1699,9 +1729,6 @@ class TestSalesInvoice(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save()
def test_deferred_revenue(self): def test_deferred_revenue(self):
deferred_account = create_account(account_name="Deferred Revenue", deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company") parent_account="Current Liabilities - _TC", company="_Test Company")
@ -1720,8 +1747,6 @@ class TestSalesInvoice(unittest.TestCase):
si.save() si.save()
si.submit() si.submit()
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
pda1 = frappe.get_doc(dict( pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting', doctype='Process Deferred Accounting',
posting_date=nowdate(), posting_date=nowdate(),
@ -1745,51 +1770,53 @@ class TestSalesInvoice(unittest.TestCase):
check_gl_entries(self, si.name, expected_gle, "2019-01-30") check_gl_entries(self, si.name, expected_gle, "2019-01-30")
def test_deferred_error_email(self): def test_fixed_deferred_revenue(self):
deferred_account = create_account(account_name="Deferred Revenue", deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company") parent_account="Current Liabilities - _TC", company="_Test Company")
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.book_deferred_entries_based_on = 'Months'
acc_settings.save()
item = create_item("_Test Item for Deferred Accounting") item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_revenue = 1 item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_account item.deferred_revenue_account = deferred_account
item.no_of_months = 12 item.no_of_months = 12
item.save() item.save()
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True) si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True)
si.items[0].enable_deferred_revenue = 1 si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-10" si.items[0].service_start_date = "2019-01-16"
si.items[0].service_end_date = "2019-03-15" si.items[0].service_end_date = "2019-03-31"
si.items[0].deferred_revenue_account = deferred_account si.items[0].deferred_revenue_account = deferred_account
si.save() si.save()
si.submit() si.submit()
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income pda1 = frappe.get_doc(dict(
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', doctype='Process Deferred Accounting',
posting_date=nowdate(), posting_date='2019-03-31',
start_date="2019-01-01", start_date="2019-01-01",
end_date="2019-03-31", end_date="2019-03-31",
type="Income", type="Income",
company="_Test Company" company="_Test Company"
)) ))
pda.insert() pda1.insert()
pda.submit() pda1.submit()
email = frappe.db.sql(""" select name from `tabEmail Queue` expected_gle = [
where message like %(txt)s """, { [deferred_account, 10000.0, 0.0, "2019-01-31"],
'txt': "%%%s%%" % "Error while processing deferred accounting for {0}".format(pda.name) ["Sales - _TC", 0.0, 10000.0, "2019-01-31"],
}) [deferred_account, 20000.0, 0.0, "2019-02-28"],
["Sales - _TC", 0.0, 20000.0, "2019-02-28"],
[deferred_account, 20000.0, 0.0, "2019-03-31"],
["Sales - _TC", 0.0, 20000.0, "2019-03-31"]
]
self.assertTrue(email) check_gl_entries(self, si.name, expected_gle, "2019-01-30")
acc_settings.load_from_db() acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.acc_frozen_upto = None acc_settings.book_deferred_entries_based_on = 'Days'
acc_settings.save() acc_settings.save()
def test_inter_company_transaction(self): def test_inter_company_transaction(self):

View File

@ -94,6 +94,7 @@
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
"project",
"section_break_54", "section_break_54",
"page_break" "page_break"
], ],
@ -783,12 +784,18 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Finance Book", "label": "Finance Book",
"options": "Finance Book" "options": "Finance Book"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2019-12-04 12:22:38.517710", "modified": "2020-03-11 12:24:41.749986",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",

View File

@ -45,7 +45,9 @@ class ShippingRule(Document):
shipping_amount = 0.0 shipping_amount = 0.0
by_value = False by_value = False
self.validate_countries(doc) if doc.get_shipping_address():
# validate country only if there is address
self.validate_countries(doc)
if self.calculate_based_on == 'Net Total': if self.calculate_based_on == 'Net Total':
value = doc.base_net_total value = doc.base_net_total

View File

@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No
if company: if company:
condition += "and company =%s" % (company) condition += "and company =%s" % (company)
if from_date and to_date: if from_date and to_date:
condition += "and posting_date between %s and %s" % (company, from_date, to_date) condition += "and posting_date between %s and %s" % (from_date, to_date)
## Appending the same supplier again if length of suppliers list is 1 ## Appending the same supplier again if length of suppliers list is 1
## since tuple of single element list contains None, For example ('Test Supplier 1', ) ## since tuple of single element list contains None, For example ('Test Supplier 1', )

View File

@ -140,10 +140,8 @@ 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.validate() gle.insert()
gle.db_insert()
gle.run_method("on_update_with_args", adv_adj, update_outstanding) gle.run_method("on_update_with_args", adv_adj, update_outstanding)
gle.flags.ignore_validate = True
gle.submit() gle.submit()
# check against budget # check against budget

View File

@ -0,0 +1,50 @@
{
"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-07-08 14:06:09.033880",
"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, Taxation, and more.",
"success_message": "The Accounts Module is all set up!",
"title": "Let's Set Up Your Accounts and Taxes."
}

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-06-01 13:16:19.731719",
"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-12 18:16:06.624554",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-12 18:30:02.489949",
"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

@ -21,7 +21,7 @@ def reconcile(bank_transaction, payment_doctype, payment_name):
if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount: if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount:
frappe.throw(_("The unallocated amount of Payment Entry {0} \ frappe.throw(_("The unallocated amount of Payment Entry {0} \
is greater than the Bank Transaction's unallocated amount").format(payment_name)) is greater than the Bank Transaction's unallocated amount").format(payment_name))
if transaction.unallocated_amount == 0: if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled")) frappe.throw(_("This bank transaction is already fully reconciled"))
@ -289,6 +289,7 @@ def get_matching_transactions_payments(description_matching):
else: else:
return [] return []
@frappe.whitelist()
def payment_entry_query(doctype, txt, searchfield, start, page_len, filters): def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
if not account: if not account:
@ -317,6 +318,7 @@ def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
} }
) )
@frappe.whitelist()
def journal_entry_query(doctype, txt, searchfield, start, page_len, filters): def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
@ -352,6 +354,7 @@ def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
} }
) )
@frappe.whitelist()
def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters): def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" return frappe.db.sql("""
SELECT SELECT

View File

@ -388,7 +388,7 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
args = { args = {
party_type.lower(): party, party_type.lower(): party,
"company": company "company": company
} }
if tax_category: if tax_category:
@ -602,10 +602,14 @@ def get_party_shipping_address(doctype, name):
else: else:
return '' return ''
def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None):
cond = "1=1" cond = "1=1"
if posting_date: if posting_date:
cond = "posting_date <= '{0}'".format(posting_date) if future_payment:
cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date)
else:
cond = "posting_date <= '{0}'".format(posting_date)
if company: if company:
cond += "and company = '{0}'".format(company) cond += "and company = '{0}'".format(company)

View File

@ -61,7 +61,7 @@ def make_sales_invoice():
debit_to = 'Debtors - _TC2', debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2', income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2', expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2') cost_center = 'Main - _TC2')

View File

@ -17,41 +17,6 @@ frappe.query_reports["Accounts Payable"] = {
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.get_today() "default": frappe.datetime.get_today()
}, },
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date\nSupplier Invoice Date',
"default": "Due Date"
},
{
"fieldname":"range1",
"label": __("Ageing Range 1"),
"fieldtype": "Int",
"default": "30",
"reqd": 1
},
{
"fieldname":"range2",
"label": __("Ageing Range 2"),
"fieldtype": "Int",
"default": "60",
"reqd": 1
},
{
"fieldname":"range3",
"label": __("Ageing Range 3"),
"fieldtype": "Int",
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{ {
"fieldname":"finance_book", "fieldname":"finance_book",
"label": __("Finance Book"), "label": __("Finance Book"),
@ -88,6 +53,41 @@ frappe.query_reports["Accounts Payable"] = {
} }
} }
}, },
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date\nSupplier Invoice Date',
"default": "Due Date"
},
{
"fieldname":"range1",
"label": __("Ageing Range 1"),
"fieldtype": "Int",
"default": "30",
"reqd": 1
},
{
"fieldname":"range2",
"label": __("Ageing Range 2"),
"fieldtype": "Int",
"default": "60",
"reqd": 1
},
{
"fieldname":"range3",
"label": __("Ageing Range 3"),
"fieldtype": "Int",
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{ {
"fieldname":"payment_terms_template", "fieldname":"payment_terms_template",
"label": __("Payment Terms Template"), "label": __("Payment Terms Template"),
@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Payable', 9);
frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -17,41 +17,6 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.get_today() "default": frappe.datetime.get_today()
}, },
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date',
"default": "Due Date"
},
{
"fieldname":"range1",
"label": __("Ageing Range 1"),
"fieldtype": "Int",
"default": "30",
"reqd": 1
},
{
"fieldname":"range2",
"label": __("Ageing Range 2"),
"fieldtype": "Int",
"default": "60",
"reqd": 1
},
{
"fieldname":"range3",
"label": __("Ageing Range 3"),
"fieldtype": "Int",
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{ {
"fieldname":"finance_book", "fieldname":"finance_book",
"label": __("Finance Book"), "label": __("Finance Book"),
@ -101,6 +66,41 @@ frappe.query_reports["Accounts Receivable"] = {
} }
} }
}, },
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date',
"default": "Due Date"
},
{
"fieldname":"range1",
"label": __("Ageing Range 1"),
"fieldtype": "Int",
"default": "30",
"reqd": 1
},
{
"fieldname":"range2",
"label": __("Ageing Range 2"),
"fieldtype": "Int",
"default": "60",
"reqd": 1
},
{
"fieldname":"range3",
"label": __("Ageing Range 3"),
"fieldtype": "Int",
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{ {
"fieldname":"customer_group", "fieldname":"customer_group",
"label": __("Customer Group"), "label": __("Customer Group"),
@ -113,12 +113,6 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Link", "fieldtype": "Link",
"options": "Payment Terms Template" "options": "Payment Terms Template"
}, },
{
"fieldname":"territory",
"label": __("Territory"),
"fieldtype": "Link",
"options": "Territory"
},
{ {
"fieldname":"sales_partner", "fieldname":"sales_partner",
"label": __("Sales Partner"), "label": __("Sales Partner"),
@ -131,6 +125,12 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Link", "fieldtype": "Link",
"options": "Sales Person" "options": "Sales Person"
}, },
{
"fieldname":"territory",
"label": __("Territory"),
"fieldtype": "Link",
"options": "Territory"
},
{ {
"fieldname": "group_by_party", "fieldname": "group_by_party",
"label": __("Group By Customer"), "label": __("Group By Customer"),
@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Receivable', 9);
frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -169,9 +169,11 @@ class ReceivablePayableReport(object):
def append_subtotal_row(self, party): def append_subtotal_row(self, party):
sub_total_row = self.total_row_map.get(party) sub_total_row = self.total_row_map.get(party)
self.data.append(sub_total_row)
self.data.append({}) if sub_total_row:
self.update_sub_total_row(sub_total_row, 'Total') self.data.append(sub_total_row)
self.data.append({})
self.update_sub_total_row(sub_total_row, 'Total')
def get_voucher_balance(self, gle): def get_voucher_balance(self, gle):
if self.filters.get("sales_person"): if self.filters.get("sales_person"):
@ -232,7 +234,8 @@ class ReceivablePayableReport(object):
if self.filters.get('group_by_party'): if self.filters.get('group_by_party'):
self.append_subtotal_row(self.previous_party) self.append_subtotal_row(self.previous_party)
self.data.append(self.total_row_map.get('Total')) if self.data:
self.data.append(self.total_row_map.get('Total'))
def append_row(self, row): def append_row(self, row):
self.allocate_future_payments(row) self.allocate_future_payments(row)
@ -534,7 +537,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 +549,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
@ -559,6 +562,14 @@ class ReceivablePayableReport(object):
conditions, values = self.prepare_conditions() conditions, values = self.prepare_conditions()
order_by = self.get_order_by_condition() order_by = self.get_order_by_condition()
if self.filters.show_future_payments:
values.insert(2, self.filters.report_date)
date_condition = """AND (posting_date <= %s
OR (against_voucher IS NULL AND DATE(creation) <= %s))"""
else:
date_condition = "AND posting_date <=%s"
if self.filters.get(scrub(self.party_type)): if self.filters.get(scrub(self.party_type)):
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
else: else:
@ -574,9 +585,8 @@ class ReceivablePayableReport(object):
docstatus < 2 docstatus < 2
and party_type=%s and party_type=%s
and (party is not null and party != '') and (party is not null and party != '')
and posting_date <= %s {1} {2} {3}"""
{1} {2}""" .format(select_fields, date_condition, conditions, order_by), values, as_dict=True)
.format(select_fields, conditions, order_by), values, as_dict=True)
def get_sales_invoices_or_customers_based_on_sales_person(self): def get_sales_invoices_or_customers_based_on_sales_person(self):
if self.filters.get("sales_person"): if self.filters.get("sales_person"):

View File

@ -63,7 +63,7 @@ def make_sales_invoice():
debit_to = 'Debtors - _TC2', debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2', income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2', expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2', cost_center = 'Main - _TC2',
do_not_save=1) do_not_save=1)
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30)) si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
@ -83,14 +83,14 @@ def make_payment(docname):
def make_credit_note(docname): def make_credit_note(docname):
create_sales_invoice(company="_Test Company 2", create_sales_invoice(company="_Test Company 2",
customer = '_Test Customer 2', customer = '_Test Customer 2',
currency = 'EUR', currency = 'EUR',
qty = -1, qty = -1,
warehouse = 'Finished Goods - _TC2', warehouse = 'Finished Goods - _TC2',
debit_to = 'Debtors - _TC2', debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2', income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2', expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2', cost_center = 'Main - _TC2',
is_return = 1, is_return = 1,
return_against = docname) return_against = docname)

View File

@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"fieldname":"based_on_payment_terms", "fieldname":"based_on_payment_terms",
"label": __("Based On Payment Terms"), "label": __("Based On Payment Terms"),
"fieldtype": "Check", "fieldtype": "Check",
} },
{
"fieldname":"show_future_payments",
"label": __("Show Future Payments"),
"fieldtype": "Check",
},
], ],
onload: function(report) { onload: function(report) {
@ -122,11 +127,4 @@ frappe.query_reports["Accounts Receivable Summary"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Receivable Summary', 9);
frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

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, self.filters.company) or {} self.filters.report_date, self.filters.show_future_payments, 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

@ -93,7 +93,7 @@ def get_assets(filters):
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category, from (SELECT a.asset_category,
ifnull(sum(case when ds.schedule_date < %(from_date)s then ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
ds.depreciation_amount ds.depreciation_amount
else else
0 0
@ -111,13 +111,11 @@ def get_assets(filters):
0 0
end), 0) as depreciation_amount_during_the_period end), 0) as depreciation_amount_during_the_period
from `tabAsset` a, `tabDepreciation Schedule` ds from `tabAsset` a, `tabDepreciation Schedule` ds
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != ''
group by a.asset_category group by a.asset_category
union union
SELECT a.asset_category, SELECT a.asset_category,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s)
then
0 0
else else
a.opening_accumulated_depreciation a.opening_accumulated_depreciation

View File

@ -4,6 +4,8 @@
frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
erpnext.utils.add_dimensions('Balance Sheet', 10);
frappe.query_reports["Balance Sheet"]["filters"].push({ frappe.query_reports["Balance Sheet"]["filters"].push({
"fieldname": "accumulated_values", "fieldname": "accumulated_values",
"label": __("Accumulated Values"), "label": __("Accumulated Values"),

View File

@ -29,41 +29,76 @@ def execute(filters=None):
for dimension in dimensions: for dimension in dimensions:
dimension_items = cam_map.get(dimension) dimension_items = cam_map.get(dimension)
if dimension_items: if dimension_items:
for account, monthwise_data in iteritems(dimension_items): data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, 0)
row = [dimension, account] else:
totals = [0, 0, 0] DCC_allocation = frappe.db.sql('''SELECT parent, sum(percentage_allocation) as percentage_allocation
for year in get_fiscal_years(filters): FROM `tabDistributed Cost Center`
last_total = 0 WHERE cost_center IN %(dimension)s
for relevant_months in period_month_ranges: AND parent NOT IN %(dimension)s
period_data = [0, 0, 0] GROUP BY parent''',{'dimension':[dimension]})
for month in relevant_months: if DCC_allocation:
if monthwise_data.get(year[0]): filters['budget_against_filter'] = [DCC_allocation[0][0]]
month_data = monthwise_data.get(year[0]).get(month, {}) cam_map = get_dimension_account_month_map(filters)
for i, fieldname in enumerate(["target", "actual", "variance"]): dimension_items = cam_map.get(DCC_allocation[0][0])
value = flt(month_data.get(fieldname)) if dimension_items:
period_data[i] += value data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1])
totals[i] += value
period_data[0] += last_total chart = get_chart_data(filters, columns, data)
if filters.get("show_cumulative"): return columns, data, None, chart
last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1] def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
row += period_data
totals[2] = totals[0] - totals[1]
if filters["period"] != "Yearly":
row += totals
data.append(row)
return columns, data for account, monthwise_data in iteritems(dimension_items):
row = [dimension, account]
totals = [0, 0, 0]
for year in get_fiscal_years(filters):
last_total = 0
for relevant_months in period_month_ranges:
period_data = [0, 0, 0]
for month in relevant_months:
if monthwise_data.get(year[0]):
month_data = monthwise_data.get(year[0]).get(month, {})
for i, fieldname in enumerate(["target", "actual", "variance"]):
value = flt(month_data.get(fieldname))
period_data[i] += value
totals[i] += value
period_data[0] += last_total
if DCC_allocation:
period_data[0] = period_data[0]*(DCC_allocation/100)
period_data[1] = period_data[1]*(DCC_allocation/100)
if(filters.get("show_cumulative")):
last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1]
row += period_data
totals[2] = totals[0] - totals[1]
if filters["period"] != "Yearly" :
row += totals
data.append(row)
return data
def get_columns(filters): def get_columns(filters):
columns = [ columns = [
_(filters.get("budget_against")) {
+ ":Link/%s:150" % (filters.get("budget_against")), 'label': _(filters.get("budget_against")),
_("Account") + ":Link/Account:150" '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
@ -79,7 +114,12 @@ def get_columns(filters):
_("Variance ") + " " + 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 [ for label in [
_("Budget") + " (%s)" + " " + str(year[0]), _("Budget") + " (%s)" + " " + str(year[0]),
@ -95,14 +135,23 @@ def get_columns(filters):
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 + [ for label in [_("Total Budget"), _("Total Actual"), _("Total Variance")]:
_("Total Budget") + ":Float:150", columns.append({
_("Total Actual") + ":Float:150", 'label': label,
_("Total Variance") + ":Float:150" 'fieldtype': 'Float',
] 'fieldname': frappe.scrub(label),
'width': 150
})
return columns
else: else:
return columns return columns
@ -173,7 +222,7 @@ def get_dimension_target_details(filters):
filters.budget_against, filters.budget_against,
filters.company, filters.company,
] ]
+ filters.get("budget_against_filter") + (filters.get("budget_against_filter") or [])
), as_dict=True) ), as_dict=True)
@ -305,3 +354,49 @@ def get_fiscal_years(filters):
}) })
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

@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Cash Flow"] = $.extend({}, frappe.query_reports["Cash Flow"] = $.extend({},
erpnext.financial_statements); erpnext.financial_statements);
erpnext.utils.add_dimensions('Cash Flow', 10);
// The last item in the array is the definition for Presentation Currency // The last item in the array is the definition for Presentation Currency
// filter. It won't be used in cash flow for now so we pop it. Please take // filter. It won't be used in cash flow for now so we pop it. Please take
// of this if you are working here. // of this if you are working here.

View File

@ -334,10 +334,9 @@ def compute_data(filters, company_currency, profit_data, period_list, light_mapp
def execute(filters=None): def execute(filters=None):
if not filters.periodicity: filters.periodicity = "Monthly" if not filters.periodicity: filters.periodicity = "Monthly"
period_list = get_period_list( period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
filters.from_fiscal_year, filters.to_fiscal_year, filters.periodicity, filters.period_start_date, filters.period_end_date, filters.filter_based_on,
filters.accumulated_values, filters.company filters.periodicity, company=filters.company)
)
mappers = get_mappers_from_db() mappers = get_mappers_from_db()
@ -396,7 +395,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate
gl_sum = frappe.db.sql_list(""" gl_sum = frappe.db.sql_list("""
select sum(credit) - sum(debit) select sum(credit) - sum(debit)
from `tabGL Entry` from `tabGL Entry`
where company=%s and posting_date >= %s and posting_date <= %s where company=%s and posting_date >= %s and posting_date <= %s
and voucher_type != 'Period Closing Voucher' and voucher_type != 'Period Closing Voucher'
and account in ( SELECT name FROM tabAccount WHERE name IN (%s) and account in ( SELECT name FROM tabAccount WHERE name IN (%s)
OR parent_account IN (%s)) OR parent_account IN (%s))
@ -405,7 +404,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate
gl_sum = frappe.db.sql_list(""" gl_sum = frappe.db.sql_list("""
select sum(credit) - sum(debit) select sum(credit) - sum(debit)
from `tabGL Entry` from `tabGL Entry`
where company=%s and posting_date >= %s and posting_date <= %s where company=%s and posting_date >= %s and posting_date <= %s
and voucher_type != 'Period Closing Voucher' and voucher_type != 'Period Closing Voucher'
and account in ( SELECT name FROM tabAccount WHERE name IN (%s) and account in ( SELECT name FROM tabAccount WHERE name IN (%s)
OR parent_account IN (%s)) OR parent_account IN (%s))

View File

@ -33,7 +33,6 @@ frappe.query_reports["Consolidated Financial Statement"] = {
"fieldname":"period_start_date", "fieldname":"period_start_date",
"label": __("Start Date"), "label": __("Start Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.nowdate(),
"hidden": 1, "hidden": 1,
"reqd": 1 "reqd": 1
}, },
@ -41,7 +40,6 @@ frappe.query_reports["Consolidated Financial Statement"] = {
"fieldname":"period_end_date", "fieldname":"period_end_date",
"label": __("End Date"), "label": __("End Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12),
"hidden": 1, "hidden": 1,
"reqd": 1 "reqd": 1
}, },
@ -106,5 +104,16 @@ frappe.query_reports["Consolidated Financial Statement"] = {
value = $value.wrap("<p></p>").parent().html(); value = $value.wrap("<p></p>").parent().html();
} }
return value; return value;
},
onload: function() {
let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
frappe.query_report.set_filter_value({
period_start_date: fy.year_start_date,
period_end_date: fy.year_end_date
});
});
} }
} }

View File

@ -44,7 +44,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for(let j=0, k=data.length-1; j<k; j++) { %} {% for(let j=0, k=data.length; j<k; j++) { %}
{% {%
var row = data[j]; var row = data[j];
var row_class = data[j].parent_account ? "" : "financial-statements-important"; var row_class = data[j].parent_account ? "" : "financial-statements-important";

View File

@ -8,6 +8,7 @@ from __future__ import unicode_literals
import re import re
from past.builtins import cmp from past.builtins import cmp
import functools import functools
import math
import frappe, erpnext import frappe, erpnext
from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
@ -19,7 +20,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)"""
@ -45,10 +46,7 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
start_date = year_start_date start_date = year_start_date
months = get_months(year_start_date, year_end_date) months = get_months(year_start_date, year_end_date)
if (months // months_to_add) != (months / months_to_add): for i in range(math.ceil(months / months_to_add)):
months += months_to_add
for i in range(months // months_to_add):
period = frappe._dict({ period = frappe._dict({
"from_date": start_date "from_date": start_date
}) })
@ -56,9 +54,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
to_date = add_months(start_date, months_to_add) to_date = add_months(start_date, months_to_add)
start_date = to_date start_date = to_date
if to_date == get_first_day(to_date): # Subtract one day from to_date, as it may be first day in next fiscal year or month
# if to_date is the first day, get the last day of previous month to_date = add_days(to_date, -1)
to_date = add_days(to_date, -1)
if to_date <= year_end_date: if to_date <= year_end_date:
# the normal case # the normal case
@ -67,8 +64,9 @@ 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
period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] if not ignore_fiscal_year:
period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] 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_list.append(period) period_list.append(period)
@ -386,11 +384,43 @@ def set_gl_entries_by_account(
key: value key: value
}) })
distributed_cost_center_query = ""
if filters and filters.get('cost_center'):
distributed_cost_center_query = """
UNION ALL
SELECT posting_date,
account,
debit*(DCC_allocation.percentage_allocation/100) as debit,
credit*(DCC_allocation.percentage_allocation/100) as credit,
is_opening,
fiscal_year,
debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency,
account_currency
FROM `tabGL Entry`,
(
SELECT parent, sum(percentage_allocation) as percentage_allocation
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(cost_center)s
AND parent NOT IN %(cost_center)s
GROUP BY parent
) as DCC_allocation
WHERE company=%(company)s
{additional_conditions}
AND posting_date <= %(to_date)s
AND is_cancelled = 0
AND cost_center = DCC_allocation.parent
""".format(additional_conditions=additional_conditions.replace("and cost_center in %(cost_center)s ", ''))
gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
where company=%(company)s where company=%(company)s
{additional_conditions} {additional_conditions}
and posting_date <= %(to_date)s and posting_date <= %(to_date)s
order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec and is_cancelled = 0
{distributed_cost_center_query}
order by account, posting_date""".format(
additional_conditions=additional_conditions,
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
if filters and filters.get('presentation_currency'): if filters and filters.get('presentation_currency'):
convert_to_presentation_currency(gl_entries, get_currency(filters)) convert_to_presentation_currency(gl_entries, get_currency(filters))
@ -488,4 +518,4 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None):
"width": 150 "width": 150
}) })
return columns return columns

View File

@ -164,12 +164,5 @@ frappe.query_reports["General Ledger"] = {
] ]
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('General Ledger', 15)
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -128,18 +128,53 @@ def get_gl_entries(filters):
filters['company_fb'] = frappe.db.get_value("Company", filters['company_fb'] = frappe.db.get_value("Company",
filters.get("company"), 'default_finance_book') filters.get("company"), 'default_finance_book')
distributed_cost_center_query = ""
if filters and filters.get('cost_center'):
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """
distributed_cost_center_query = """
UNION ALL
SELECT name as gl_entry,
posting_date,
account,
party_type,
party,
voucher_type,
voucher_no,
cost_center, project,
against_voucher_type,
against_voucher,
account_currency,
remarks, against,
is_opening, `tabGL Entry`.creation {select_fields_with_percentage}
FROM `tabGL Entry`,
(
SELECT parent, sum(percentage_allocation) as percentage_allocation
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(cost_center)s
AND parent NOT IN %(cost_center)s
GROUP BY parent
) as DCC_allocation
WHERE company=%(company)s
{conditions}
AND posting_date <= %(to_date)s
AND cost_center = DCC_allocation.parent
""".format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", ''))
gl_entries = frappe.db.sql( gl_entries = frappe.db.sql(
""" """
select select
name as gl_entry, posting_date, account, party_type, party, name as gl_entry, posting_date, account, party_type, party,
voucher_type, voucher_no, cost_center, project, voucher_type, voucher_no, cost_center, project,
against_voucher_type, against_voucher, account_currency, against_voucher_type, against_voucher, account_currency,
remarks, against, is_opening {select_fields} remarks, against, is_opening, creation {select_fields}
from `tabGL Entry` from `tabGL Entry`
where company=%(company)s {conditions} where company=%(company)s {conditions}
{distributed_cost_center_query}
{order_by_statement} {order_by_statement}
""".format( """.format(
select_fields=select_fields, conditions=get_conditions(filters), select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query,
order_by_statement=order_by_statement order_by_statement=order_by_statement
), ),
filters, as_dict=1) filters, as_dict=1)
@ -296,7 +331,7 @@ 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: if data[key].against_voucher and gle.against_voucher:
data[key].against_voucher += ', ' + 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)

View File

@ -4,11 +4,18 @@
frappe.query_reports["Item-wise Purchase Register"] = { frappe.query_reports["Item-wise Purchase Register"] = {
"filters": [ "filters": [
{ {
"fieldname":"date_range", "fieldname":"from_date",
"label": __("Date Range"), "label": __("From Date"),
"fieldtype": "DateRange", "fieldtype": "Date",
"default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1 "reqd": 1,
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1,
}, },
{ {
"fieldname": "item_code", "fieldname": "item_code",

View File

@ -14,7 +14,6 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {} if not filters: filters = {}
filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]})
columns = get_columns(additional_table_columns, filters) columns = get_columns(additional_table_columns, filters)
company_currency = erpnext.get_company_currency(filters.company) company_currency = erpnext.get_company_currency(filters.company)
@ -266,13 +265,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]

View File

@ -4,11 +4,18 @@
frappe.query_reports["Item-wise Sales Register"] = { frappe.query_reports["Item-wise Sales Register"] = {
"filters": [ "filters": [
{ {
"fieldname": "date_range", "fieldname":"from_date",
"label": __("Date Range"), "label": __("From Date"),
"fieldtype": "DateRange", "fieldtype": "Date",
"default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1 "reqd": 1,
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1,
}, },
{ {
"fieldname": "customer", "fieldname": "customer",

View File

@ -14,7 +14,6 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {} if not filters: filters = {}
filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]})
columns = get_columns(additional_table_columns, filters) columns = get_columns(additional_table_columns, filters)
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
@ -224,7 +223,7 @@ def get_columns(additional_table_columns, filters):
} }
] ]
if filters.get('group_by') != 'Terriotory': if filters.get('group_by') != 'Territory':
columns.extend([ columns.extend([
{ {
'label': _("Territory"), 'label': _("Territory"),
@ -305,13 +304,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]
@ -537,6 +529,13 @@ def get_tax_accounts(item_list, columns, company_currency,
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]

View File

@ -1,27 +0,0 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-02-21 14:26:44",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-11-06 13:04:51.559061",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Ordered Items To Be Billed",
"owner": "Administrator",
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name:150\",\n`tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (`tabSales Order Item`.base_amount - (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Closed\"\n and `tabSales Order Item`.amount > 0\n and `tabSales Order Item`.billed_amt < `tabSales Order Item`.amount\norder by `tabSales Order`.transaction_date asc",
"ref_doctype": "Sales Invoice",
"report_name": "Ordered Items To Be Billed",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
}
]
}

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 [
_("Sales Order") + ":Link/Sales Order:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
]
def get_args():
return {'doctype': 'Sales Order', 'party': 'customer',
'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'}

View File

@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Profit and Loss Statement"] = $.extend({}, frappe.query_reports["Profit and Loss Statement"] = $.extend({},
erpnext.financial_statements); erpnext.financial_statements);
erpnext.utils.add_dimensions('Profit and Loss Statement', 10);
frappe.query_reports["Profit and Loss Statement"]["filters"].push( frappe.query_reports["Profit and Loss Statement"]["filters"].push(
{ {
"fieldname": "project", "fieldname": "project",

View File

@ -105,6 +105,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name):
def prepare_data(accounts, filters, total_row, parent_children_map, based_on): def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
data = [] data = []
new_accounts = accounts
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
for d in accounts: for d in accounts:
@ -118,6 +119,19 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
"currency": company_currency, "currency": company_currency,
"based_on": based_on "based_on": based_on
} }
if based_on == 'cost_center':
cost_center_doc = frappe.get_doc("Cost Center",d.name)
if not cost_center_doc.enable_distributed_cost_center:
DCC_allocation = frappe.db.sql("""SELECT parent, sum(percentage_allocation) as percentage_allocation
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(cost_center)s
AND parent NOT IN %(cost_center)s
GROUP BY parent""",{'cost_center': [d.name]})
if DCC_allocation:
for account in new_accounts:
if account['name'] == DCC_allocation[0][0]:
for value in value_fields:
d[value] += account[value]*(DCC_allocation[0][1]/100)
for key in value_fields: for key in value_fields:
row[key] = flt(d.get(key, 0.0), 3) row[key] = flt(d.get(key, 0.0), 3)

View File

@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Purchase Order Items To Be Billed"] = {
"filters": [
]
}

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

@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = {
] ]
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Purchase Register', 7);
frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = {
] ]
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Sales Register', 7);
frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

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

@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"initial_depth": 3 "initial_depth": 3
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Trial Balance', 6);
frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});
}); });

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