Merge branch 'develop' into email-camp-end-date

This commit is contained in:
Rucha Mahabal 2020-04-23 16:00:27 +05:30 committed by GitHub
commit 78d7ed521f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 453 additions and 393 deletions

View File

@ -1,134 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_subtype",
"beta": 0,
"creation": "2018-10-25 15:46:08.054586",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account_subtype",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Account Subtype",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-25 15:47:03.841390",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Subtype",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,23 +0,0 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Account Subtype", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Account Subtype
() => frappe.tests.make('Account Subtype', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -1,8 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Account Type', {
refresh: function() {
}
});

View File

@ -1,134 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_type",
"beta": 0,
"creation": "2018-10-25 15:45:45.789963",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account_type",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Account Type",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-25 15:46:51.042604",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Type",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View File

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

View File

@ -64,13 +64,13 @@
"fieldname": "account_type",
"fieldtype": "Link",
"label": "Account Type",
"options": "Account Type"
"options": "Bank Account Type"
},
{
"fieldname": "account_subtype",
"fieldtype": "Link",
"label": "Account Subtype",
"options": "Account Subtype"
"options": "Bank Account Subtype"
},
{
"fieldname": "column_break_7",
@ -200,7 +200,7 @@
}
],
"links": [],
"modified": "2020-01-30 20:42:26.458316",
"modified": "2020-04-06 21:00:45.379804",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account",

View File

@ -1,7 +1,7 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Account Subtype', {
frappe.ui.form.on('Bank Account Subtype', {
refresh: function() {
}

View File

@ -0,0 +1,134 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_subtype",
"beta": 0,
"creation": "2018-10-25 15:46:08.054586",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account_subtype",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Account Subtype",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-25 15:47:03.841390",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account Subtype",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View File

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

View File

@ -2,15 +2,15 @@
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Account Type", function (assert) {
QUnit.test("test: Bank Account Subtype", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Account Type
() => frappe.tests.make('Account Type', [
// insert a new Bank Account Subtype
() => frappe.tests.make('Bank Account Subtype', [
// values to be set
{key: 'value'}
]),

View File

@ -5,5 +5,5 @@ from __future__ import unicode_literals
import unittest
class TestAccountSubtype(unittest.TestCase):
class TestBankAccountSubtype(unittest.TestCase):
pass

View File

@ -0,0 +1,8 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Bank Account Type', {
// refresh: function(frm) {
// }
});

View File

@ -0,0 +1,68 @@
{
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:account_type",
"creation": "2018-10-25 15:45:45.789963",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account_type"
],
"fields": [
{
"fieldname": "account_type",
"fieldtype": "Data",
"label": "Account Type",
"unique": 1
}
],
"links": [],
"modified": "2020-04-10 21:13:09.137898",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account Type",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class AccountSubtype(Document):
class BankAccountType(Document):
pass

View File

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

View File

@ -9,6 +9,7 @@ from frappe.utils import flt, getdate, add_months, get_last_day, fmt_money, nowd
from frappe.model.naming import make_autoname
from erpnext.accounts.utils import get_fiscal_year
from frappe.model.document import Document
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
class BudgetError(frappe.ValidationError): pass
class DuplicateBudgetError(frappe.ValidationError): pass
@ -98,30 +99,32 @@ def validate_expense_against_budget(args):
if not (args.get('account') and args.get('cost_center')) and args.item_code:
args.cost_center, args.account = get_item_details(args)
if not (args.cost_center or args.project) and not args.account:
if not args.account:
return
for budget_against in ['project', 'cost_center']:
for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
if (args.get(budget_against) and args.account
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
if args.project and budget_against == 'project':
condition = "and b.project=%s" % frappe.db.escape(args.project)
args.budget_against_field = "Project"
doctype = frappe.unscrub(budget_against)
elif args.cost_center and budget_against == 'cost_center':
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"])
condition = """and exists(select name from `tabCost Center`
where lft<=%s and rgt>=%s and name=b.cost_center)""" % (cc_lft, cc_rgt)
args.budget_against_field = "Cost Center"
if frappe.get_cached_value('DocType', doctype, 'is_tree'):
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
condition = """and exists(select name from `tab%s`
where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
args.is_tree = True
else:
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
args.is_tree = False
args.budget_against = args.get(budget_against)
args.budget_against_field = budget_against
args.budget_against_doctype = doctype
budget_records = frappe.db.sql("""
select
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
ifnull(b.applicable_on_material_request, 0) as for_material_request,
ifnull(applicable_on_purchase_order,0) as for_purchase_order,
ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
@ -132,9 +135,7 @@ def validate_expense_against_budget(args):
b.name=ba.parent and b.fiscal_year=%s
and ba.account=%s and b.docstatus=1
{condition}
""".format(condition=condition,
budget_against_field=frappe.scrub(args.get("budget_against_field"))),
(args.fiscal_year, args.account), as_dict=True)
""".format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
if budget_records:
validate_budget_records(args, budget_records)
@ -230,10 +231,10 @@ def get_ordered_amount(args, budget):
def get_other_condition(args, budget, for_doc):
condition = "expense_account = '%s'" % (args.expense_account)
budget_against_field = frappe.scrub(args.get("budget_against_field"))
budget_against_field = args.get("budget_against_field")
if budget_against_field and args.get(budget_against_field):
condition += " and child.%s = '%s'" %(budget_against_field, args.get(budget_against_field))
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
if args.get('fiscal_year'):
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
@ -246,19 +247,30 @@ def get_other_condition(args, budget, for_doc):
return condition
def get_actual_expense(args):
if not args.budget_against_doctype:
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
budget_against_field = args.get('budget_against_field')
condition1 = " and gle.posting_date <= %(month_end_date)s" \
if args.get("month_end_date") else ""
if args.budget_against_field == "Cost Center":
lft_rgt = frappe.db.get_value(args.budget_against_field,
args.budget_against, ["lft", "rgt"], as_dict=1)
if args.is_tree:
lft_rgt = frappe.db.get_value(args.budget_against_doctype,
args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
args.update(lft_rgt)
condition2 = """and exists(select name from `tabCost Center`
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)"""
elif args.budget_against_field == "Project":
condition2 = "and exists(select name from `tabProject` where name=gle.project and gle.project = %(budget_against)s)"
condition2 = """and exists(select name from `tab{doctype}`
where lft>=%(lft)s and rgt<=%(rgt)s
and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
budget_against_field=budget_against_field)
else:
condition2 = """and exists(select name from `tab{doctype}`
where name=gle.{budget_against} and
gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
budget_against = budget_against_field)
return flt(frappe.db.sql("""
amount = flt(frappe.db.sql("""
select sum(gle.debit) - sum(gle.credit)
from `tabGL Entry` gle
where gle.account=%(account)s
@ -267,7 +279,9 @@ def get_actual_expense(args):
and gle.company=%(company)s
and gle.docstatus=1
{condition2}
""".format(condition1=condition1, condition2=condition2), (args))[0][0])
""".format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
return amount
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
distribution = {}

View File

@ -13,7 +13,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self):
set_total_expense_zero("2013-02-28", "Cost Center")
set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center")
@ -26,7 +26,7 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_monthly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "Cost Center")
set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center")
@ -41,7 +41,7 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_exception_approver_role(self):
set_total_expense_zero("2013-02-28", "Cost Center")
set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center")
@ -114,7 +114,7 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_monthly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "Project")
set_total_expense_zero("2013-02-28", "project")
budget = make_budget(budget_against="Project")
@ -129,7 +129,7 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_yearly_budget_crossed_stop1(self):
set_total_expense_zero("2013-02-28", "Cost Center")
set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center")
@ -141,7 +141,7 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_yearly_budget_crossed_stop2(self):
set_total_expense_zero("2013-02-28", "Project")
set_total_expense_zero("2013-02-28", "project")
budget = make_budget(budget_against="Project")
@ -153,7 +153,7 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_monthly_budget_on_cancellation1(self):
set_total_expense_zero("2013-02-28", "Cost Center")
set_total_expense_zero("2013-02-28", "cost_center")
budget = make_budget(budget_against="Cost Center")
@ -177,7 +177,7 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_monthly_budget_on_cancellation2(self):
set_total_expense_zero("2013-02-28", "Project")
set_total_expense_zero("2013-02-28", "project")
budget = make_budget(budget_against="Project")
@ -201,8 +201,8 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_monthly_budget_against_group_cost_center(self):
set_total_expense_zero("2013-02-28", "Cost Center")
set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC")
set_total_expense_zero("2013-02-28", "cost_center")
set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
@ -241,25 +241,30 @@ class TestBudget(unittest.TestCase):
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
if budget_against_field == "Project":
if budget_against_field == "project":
budget_against = "_Test Project"
else:
budget_against = budget_against_CC or "_Test Cost Center - _TC"
existing_expense = get_actual_expense(frappe._dict({
args = frappe._dict({
"account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"monthly_end_date": posting_date,
"company": "_Test Company",
"fiscal_year": "_Test Fiscal Year 2013",
"budget_against_field": budget_against_field,
"budget_against": budget_against
}))
})
if not args.get(budget_against_field):
args[budget_against_field] = budget_against
existing_expense = get_actual_expense(args)
if existing_expense:
if budget_against_field == "Cost Center":
if budget_against_field == "cost_center":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
elif budget_against_field == "Project":
elif budget_against_field == "project":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")

View File

@ -46,6 +46,7 @@ class SellingController(StockController):
set_default_income_account_for_item(self)
self.set_customer_address()
self.validate_for_duplicate_items()
self.validate_target_warehouse()
def set_missing_values(self, for_validate=False):
@ -403,6 +404,14 @@ class SellingController(StockController):
else:
chk_dupl_itm.append(f)
def validate_target_warehouse(self):
items = self.get("items") + (self.get("packed_items") or [])
for d in items:
if d.get("target_warehouse") and d.get("warehouse") == d.get("target_warehouse"):
warehouse = frappe.bold(d.get("target_warehouse"))
frappe.throw(_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same")
.format(d.idx, warehouse, warehouse))
def validate_items(self):
# validate items to see if they have is_sales_item enabled

View File

@ -67,11 +67,11 @@ def add_bank_accounts(response, bank, company):
frappe.throw(_("Please setup a default bank account for company {0}").format(company))
for account in response["accounts"]:
acc_type = frappe.db.get_value("Account Type", account["type"])
acc_type = frappe.db.get_value("Bank Account Type", account["type"])
if not acc_type:
add_account_type(account["type"])
acc_subtype = frappe.db.get_value("Account Subtype", account["subtype"])
acc_subtype = frappe.db.get_value("Bank Account Subtype", account["subtype"])
if not acc_subtype:
add_account_subtype(account["subtype"])
@ -106,7 +106,7 @@ def add_bank_accounts(response, bank, company):
def add_account_type(account_type):
try:
frappe.get_doc({
"doctype": "Account Type",
"doctype": "Bank Account Type",
"account_type": account_type
}).insert()
except Exception:
@ -116,7 +116,7 @@ def add_account_type(account_type):
def add_account_subtype(account_subtype):
try:
frappe.get_doc({
"doctype": "Account Subtype",
"doctype": "Bank Account Subtype",
"account_subtype": account_subtype
}).insert()
except Exception:

View File

@ -23,11 +23,11 @@ class TestPlaidSettings(unittest.TestCase):
for ba in frappe.get_all("Bank Account"):
frappe.get_doc("Bank Account", ba.name).delete()
for at in frappe.get_all("Account Type"):
frappe.get_doc("Account Type", at.name).delete()
for at in frappe.get_all("Bank Account Type"):
frappe.get_doc("Bank Account Type", at.name).delete()
for ast in frappe.get_all("Account Subtype"):
frappe.get_doc("Account Subtype", ast.name).delete()
for ast in frappe.get_all("Bank Account Subtype"):
frappe.get_doc("Bank Account Subtype", ast.name).delete()
def test_plaid_disabled(self):
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
@ -35,11 +35,11 @@ class TestPlaidSettings(unittest.TestCase):
def test_add_account_type(self):
add_account_type("brokerage")
self.assertEqual(frappe.get_doc("Account Type", "brokerage").name, "brokerage")
self.assertEqual(frappe.get_doc("Bank Account Type", "brokerage").name, "brokerage")
def test_add_account_subtype(self):
add_account_subtype("loan")
self.assertEqual(frappe.get_doc("Account Subtype", "loan").name, "loan")
self.assertEqual(frappe.get_doc("Bank Account Subtype", "loan").name, "loan")
def test_default_bank_account(self):
if not frappe.db.exists("Bank", "Citi"):

View File

@ -31,7 +31,7 @@ frappe.ui.form.on('Loan Application', {
add_toolbar_buttons: function(frm) {
if (frm.doc.status == "Approved") {
if (frm.doc.is_secured) {
if (frm.doc.is_secured_loan) {
frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => {
if (!r) {
frm.add_custom_button(__('Loan Security Pledge'), function() {

View File

@ -30,7 +30,8 @@ class LoanSecurityPledge(Document):
if not pledge.qty and not pledge.amount:
frappe.throw(_("Qty or Amount is mandatroy for loan security"))
pledge.loan_security_price = get_loan_security_price(pledge.loan_security)
if not (self.loan_application and pledge.loan_security_price):
pledge.loan_security_price = get_loan_security_price(pledge.loan_security)
if not pledge.qty:
pledge.qty = cint(pledge.amount/pledge.loan_security_price)

View File

@ -37,7 +37,7 @@ def get_loan_security_price(loan_security, valid_time=None):
}, 'loan_security_price')
if not loan_security_price:
frappe.throw(_("No valid <b>Loan Security Price</b> found for {0}").format(frappe.bold(loan_security)))
frappe.throw(_("No valid Loan Security Price found for {0}").format(frappe.bold(loan_security)))
else:
return loan_security_price

View File

@ -246,12 +246,13 @@ class BOM(WebsiteGenerator):
if rate:
d.rate = rate
d.amount = flt(d.rate) * flt(d.qty)
d.db_update()
if self.docstatus == 1:
self.flags.ignore_validate_update_after_submit = True
self.calculate_cost()
if save:
self.save()
self.db_update()
self.update_exploded_items()
# update parent BOMs

View File

@ -82,7 +82,7 @@ def enqueue_replace_bom(args):
@frappe.whitelist()
def enqueue_update_cost():
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_cost")
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_cost", timeout=40000)
frappe.msgprint(_("Queued for updating latest price in all Bill of Materials. It may take a few minutes."))
def update_latest_price_in_all_boms():
@ -98,6 +98,9 @@ def replace_bom(args):
doc.replace_bom()
def update_cost():
frappe.db.auto_commit_on_many_writes = 1
bom_list = get_boms_in_bottom_up_order()
for bom in bom_list:
frappe.get_doc("BOM", bom).update_cost(update_parent=False, from_child_bom=True)
frappe.get_doc("BOM", bom).update_cost(update_parent=False, from_child_bom=True)
frappe.db.auto_commit_on_many_writes = 0

View File

@ -5,6 +5,9 @@
from __future__ import unicode_literals
import unittest
import frappe
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
test_records = frappe.get_test_records('BOM')
@ -27,4 +30,31 @@ class TestBOMUpdateTool(unittest.TestCase):
# reverse, as it affects other testcases
update_tool.current_bom = bom_doc.name
update_tool.new_bom = current_bom
update_tool.replace_bom()
update_tool.replace_bom()
def test_bom_cost(self):
for item in ["BOM Cost Test Item 1", "BOM Cost Test Item 2", "BOM Cost Test Item 3"]:
item_doc = create_item(item, valuation_rate=100)
if item_doc.valuation_rate != 100.00:
frappe.db.set_value("Item", item_doc.name, "valuation_rate", 100)
bom_no = frappe.db.get_value('BOM', {'item': 'BOM Cost Test Item 1'}, "name")
if not bom_no:
doc = make_bom(item = 'BOM Cost Test Item 1',
raw_materials =['BOM Cost Test Item 2', 'BOM Cost Test Item 3'], currency="INR")
else:
doc = frappe.get_doc("BOM", bom_no)
self.assertEquals(doc.total_cost, 200)
frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 200)
update_cost()
doc.load_from_db()
self.assertEquals(doc.total_cost, 300)
frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 100)
update_cost()
doc.load_from_db()
self.assertEquals(doc.total_cost, 200)

View File

@ -192,9 +192,10 @@ def make_bom(**args):
args = frappe._dict(args)
bom = frappe.get_doc({
'doctype': "BOM",
'doctype': 'BOM',
'is_default': 1,
'item': args.item,
'currency': args.currency or 'USD',
'quantity': args.quantity or 1,
'company': args.company or '_Test Company'
})
@ -211,4 +212,5 @@ def make_bom(**args):
})
bom.insert(ignore_permissions=True)
bom.submit()
bom.submit()
return bom

View File

@ -18,10 +18,10 @@ def get_columns():
"""return columns"""
columns = [
_("Item") + ":Link/Item:150",
_("Description") + "::500",
_("Qty per BOM Line") + ":Float:100",
_("Required Qty") + ":Float:100",
_("In Stock Qty") + ":Float:100",
_("Description") + "::300",
_("BOM Qty") + ":Float:160",
_("Required Qty") + ":Float:120",
_("In Stock Qty") + ":Float:120",
_("Enough Parts to Build") + ":Float:200",
]
@ -59,13 +59,14 @@ def get_bom_stock(filters):
bom_item.item_code,
bom_item.description ,
bom_item.{qty_field},
bom_item.{qty_field} * {qty_to_produce},
bom_item.{qty_field} * {qty_to_produce} / bom.quantity,
sum(ledger.actual_qty) as actual_qty,
sum(FLOOR(ledger.actual_qty / (bom_item.{qty_field} * {qty_to_produce})))
sum(FLOOR(ledger.actual_qty / (bom_item.{qty_field} * {qty_to_produce} / bom.quantity)))
FROM
{table} AS bom_item
`tabBOM` AS bom INNER JOIN {table} AS bom_item
ON bom.name = bom_item.parent
LEFT JOIN `tabBin` AS ledger
ON bom_item.item_code = ledger.item_code
ON bom_item.item_code = ledger.item_code
{conditions}
WHERE
bom_item.parent = '{bom}' and bom_item.parenttype='BOM'

View File

@ -662,9 +662,11 @@ erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
erpnext.patches.v12_0.rename_account_type_doctype
erpnext.patches.v12_0.recalculate_requested_qty_in_bin
erpnext.patches.v12_0.update_healthcare_refactored_changes
erpnext.patches.v12_0.set_total_batch_quantity
erpnext.patches.v12_0.rename_mws_settings_fields
erpnext.patches.v12_0.set_updated_purpose_in_pick_list
erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign

View File

@ -0,0 +1,7 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.rename_doc('DocType', 'Account Type', 'Bank Account Type', force=True)
frappe.rename_doc('DocType', 'Account Subtype', 'Bank Account Subtype', force=True)
frappe.reload_doc('accounts', 'doctype', 'bank_account')

View File

@ -0,0 +1,71 @@
# Copyright (c) 2020, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
warehouse_perm = frappe.get_all("User Permission",
fields=["count(*) as p_count", "is_default", "user"], filters={"allow": "Warehouse"}, group_by="user")
if not warehouse_perm:
return
execute_patch = False
for perm_data in warehouse_perm:
if perm_data.p_count == 1 or (perm_data.p_count > 1 and frappe.get_all("User Permission",
filters = {"user": perm_data.user, "allow": "warehouse", "is_default": 1}, limit=1)):
execute_patch = True
break
if not execute_patch: return
for doctype in ["Sales Invoice", "Delivery Note"]:
if not frappe.get_meta(doctype + ' Item').get_field("target_warehouse").hidden: continue
cond = ""
if doctype == "Sales Invoice":
cond = " AND parent_doc.update_stock = 1"
data = frappe.db.sql(""" SELECT parent_doc.name as name, child_doc.name as child_name
FROM
`tab{doctype}` parent_doc, `tab{doctype} Item` child_doc
WHERE
parent_doc.name = child_doc.parent AND parent_doc.docstatus < 2
AND child_doc.target_warehouse is not null AND child_doc.target_warehouse != ''
AND child_doc.creation > '2020-04-16' {cond}
""".format(doctype=doctype, cond=cond), as_dict=1)
if data:
names = [d.child_name for d in data]
frappe.db.sql(""" UPDATE `tab{0} Item` set target_warehouse = null
WHERE name in ({1}) """.format(doctype, ','.join(["%s"] * len(names) )), tuple(names))
frappe.db.sql(""" UPDATE `tabPacked Item` set target_warehouse = null
WHERE parenttype = '{0}' and parent_detail_docname in ({1})
""".format(doctype, ','.join(["%s"] * len(names) )), tuple(names))
parent_names = list(set([d.name for d in data]))
for d in parent_names:
doc = frappe.get_doc(doctype, d)
if doc.docstatus != 1: continue
doc.docstatus = 2
doc.update_stock_ledger()
doc.make_gl_entries_on_cancel(repost_future_gle=False)
# update stock & gl entries for submit state of PR
doc.docstatus = 1
doc.update_stock_ledger()
doc.make_gl_entries()
if frappe.get_meta('Sales Order Item').get_field("target_warehouse").hidden:
frappe.db.sql(""" UPDATE `tabSales Order Item` set target_warehouse = null
WHERE creation > '2020-04-16' and docstatus < 2 """)
frappe.db.sql(""" UPDATE `tabPacked Item` set target_warehouse = null
WHERE creation > '2020-04-16' and docstatus < 2 and parenttype = 'Sales Order' """)

View File

@ -170,6 +170,8 @@ def get_item_warehouse_map(filters, sle):
from_date = getdate(filters.get("from_date"))
to_date = getdate(filters.get("to_date"))
float_precision = cint(frappe.db.get_default("float_precision")) or 3
for d in sle:
key = (d.company, d.item_code, d.warehouse)
if key not in iwb_map:
@ -184,7 +186,7 @@ def get_item_warehouse_map(filters, sle):
qty_dict = iwb_map[(d.company, d.item_code, d.warehouse)]
if d.voucher_type == "Stock Reconciliation":
qty_diff = flt(d.qty_after_transaction) - qty_dict.bal_qty
qty_diff = flt(d.qty_after_transaction) - flt(qty_dict.bal_qty)
else:
qty_diff = flt(d.actual_qty)
@ -195,7 +197,7 @@ def get_item_warehouse_map(filters, sle):
qty_dict.opening_val += value_diff
elif d.posting_date >= from_date and d.posting_date <= to_date:
if qty_diff > 0:
if flt(qty_diff, float_precision) >= 0:
qty_dict.in_qty += qty_diff
qty_dict.in_val += value_diff
else:
@ -206,16 +208,15 @@ def get_item_warehouse_map(filters, sle):
qty_dict.bal_qty += qty_diff
qty_dict.bal_val += value_diff
iwb_map = filter_items_with_no_transactions(iwb_map)
iwb_map = filter_items_with_no_transactions(iwb_map, float_precision)
return iwb_map
def filter_items_with_no_transactions(iwb_map):
def filter_items_with_no_transactions(iwb_map, float_precision):
for (company, item, warehouse) in sorted(iwb_map):
qty_dict = iwb_map[(company, item, warehouse)]
no_transactions = True
float_precision = cint(frappe.db.get_default("float_precision")) or 3
for key, val in iteritems(qty_dict):
val = flt(val, float_precision)
qty_dict[key] = val