Merge branch 'develop'

This commit is contained in:
Anand Doshi 2015-09-17 15:35:09 +05:30
commit 7523429ded
179 changed files with 12753 additions and 11115 deletions

View File

@ -1,6 +1,6 @@
# ERPNext - Open source ERP for small and medium-size business [![Build Status](https://travis-ci.org/frappe/erpnext.png)](https://travis-ci.org/frappe/erpnext) # ERPNext - ERP made simple
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/frappe/erpnext.png)](https://travis-ci.org/frappe/erpnext) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[https://erpnext.com](https://erpnext.com) [https://erpnext.com](https://erpnext.com)

View File

@ -1,2 +1,2 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__version__ = '6.1.1' __version__ = '6.2.0'

View File

@ -7,7 +7,7 @@
"description": "Heads (or groups) against which Accounting Entries are made and balances are maintained.", "description": "Heads (or groups) against which Accounting Entries are made and balances are maintained.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -166,6 +166,30 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.is_group==0",
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Currency",
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -402,7 +426,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 15:51:26",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account", "name": "Account",

View File

@ -7,6 +7,8 @@ from frappe.utils import cstr, cint
from frappe import throw, _ from frappe import throw, _
from frappe.model.document import Document from frappe.model.document import Document
class RootNotEditable(frappe.ValidationError): pass
class Account(Document): class Account(Document):
nsm_parent_field = 'parent_account' nsm_parent_field = 'parent_account'
@ -28,6 +30,7 @@ class Account(Document):
self.validate_warehouse_account() self.validate_warehouse_account()
self.validate_frozen_accounts_modifier() self.validate_frozen_accounts_modifier()
self.validate_balance_must_be_debit_or_credit() self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency()
def validate_parent(self): def validate_parent(self):
"""Fetch Parent Details and validate parent account""" """Fetch Parent Details and validate parent account"""
@ -67,7 +70,7 @@ class Account(Document):
# does not exists parent # does not exists parent
if frappe.db.exists("Account", self.name): if frappe.db.exists("Account", self.name):
if not frappe.db.get_value("Account", self.name, "parent_account"): if not frappe.db.get_value("Account", self.name, "parent_account"):
throw(_("Root cannot be edited.")) throw(_("Root cannot be edited."), RootNotEditable)
def validate_frozen_accounts_modifier(self): def validate_frozen_accounts_modifier(self):
old_value = frappe.db.get_value("Account", self.name, "freeze_account") old_value = frappe.db.get_value("Account", self.name, "freeze_account")
@ -87,6 +90,14 @@ class Account(Document):
elif account_balance < 0 and self.balance_must_be == "Debit": elif account_balance < 0 and self.balance_must_be == "Debit":
frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'")) frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"))
def validate_account_currency(self):
if not self.account_currency:
self.account_currency = frappe.db.get_value("Company", self.company, "default_currency")
elif self.account_currency != frappe.db.get_value("Account", self.name, "account_currency"):
if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
def convert_group_to_ledger(self): def convert_group_to_ledger(self):
if self.check_if_child_exists(): if self.check_if_child_exists():
throw(_("Account with child nodes cannot be converted to ledger")) throw(_("Account with child nodes cannot be converted to ledger"))

View File

@ -36,7 +36,8 @@ def create_charts(chart_name, company):
"is_group": is_group, "is_group": is_group,
"root_type": root_type, "root_type": root_type,
"report_type": report_type, "report_type": report_type,
"account_type": child.get("account_type") "account_type": child.get("account_type"),
"account_currency": frappe.db.get_value("Company", company, "default_currency")
}) })
if root_account: if root_account:

View File

@ -9,36 +9,39 @@ def _make_test_records(verbose):
accounts = [ accounts = [
# [account_name, parent_account, is_group] # [account_name, parent_account, is_group]
["_Test Account Bank Account", "Bank Accounts", 0, "Bank"], ["_Test Bank", "Bank Accounts", 0, "Bank", None],
["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"],
["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"],
["_Test Account Stock Expenses", "Direct Expenses", 1, None], ["_Test Account Stock Expenses", "Direct Expenses", 1, None, None],
["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable"], ["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax"], ["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None],
["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable"], ["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment"], ["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None],
["_Test Account Tax Assets", "Current Assets", 1, None, None],
["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Tax Assets", "Current Assets", 1, None], ["_Test Account Reserves and Surplus", "Current Liabilities", 0, None, None],
["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax"],
["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax"],
["_Test Account Reserves and Surplus", "Current Liabilities", 0, None], ["_Test Account Cost for Goods Sold", "Expenses", 0, None, None],
["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Cost for Goods Sold", "Expenses", 0, None], ["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax"], ["_Test Account S&H Education Cess", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax"], ["_Test Account CST", "Direct Expenses", 0, "Tax", None],
["_Test Account S&H Education Cess", "_Test Account Tax Assets", 0, "Tax"], ["_Test Account Discount", "Direct Expenses", 0, None, None],
["_Test Account CST", "Direct Expenses", 0, "Tax"], ["_Test Write Off", "Indirect Expenses", 0, None, None],
["_Test Account Discount", "Direct Expenses", 0, None],
["_Test Write Off", "Indirect Expenses", 0, None],
# related to Account Inventory Integration # related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None], ["_Test Account Stock In Hand", "Current Assets", 0, None, None],
["_Test Account Fixed Assets", "Current Assets", 0, None], ["_Test Account Fixed Assets", "Current Assets", 0, None, None],
# Receivable / Payable Account # Receivable / Payable Account
["_Test Receivable", "Current Assets", 0, "Receivable"], ["_Test Receivable", "Current Assets", 0, "Receivable", None],
["_Test Payable", "Current Liabilities", 0, "Payable"], ["_Test Payable", "Current Liabilities", 0, "Payable", None],
["_Test Receivable USD", "Current Assets", 0, "Receivable", "USD"],
["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
] ]
for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"]]: for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"]]:
@ -48,7 +51,8 @@ def _make_test_records(verbose):
"parent_account": parent_account + " - " + abbr, "parent_account": parent_account + " - " + abbr,
"company": company, "company": company,
"is_group": is_group, "is_group": is_group,
"account_type": account_type "account_type": account_type,
} for account_name, parent_account, is_group, account_type in accounts]) "account_currency": currency
} for account_name, parent_account, is_group, account_type, currency in accounts])
return test_objects return test_objects

View File

@ -16,7 +16,9 @@ class AccountsSettings(Document):
if cint(self.auto_accounting_for_stock): if cint(self.auto_accounting_for_stock):
# set default perpetual account in company # set default perpetual account in company
for company in frappe.db.sql("select name from tabCompany"): for company in frappe.db.sql("select name from tabCompany"):
frappe.get_doc("Company", company[0]).save() company = frappe.get_doc("Company", company[0])
company.flags.ignore_permissions = True
company.save()
# Create account head for warehouses # Create account head for warehouses
warehouse_list = frappe.db.sql("select name, company from tabWarehouse", as_dict=1) warehouse_list = frappe.db.sql("select name, company from tabWarehouse", as_dict=1)
@ -25,4 +27,5 @@ class AccountsSettings(Document):
frappe.throw(_("Company is missing in warehouses {0}").format(comma_and(warehouse_with_no_company))) frappe.throw(_("Company is missing in warehouses {0}").format(comma_and(warehouse_with_no_company)))
for wh in warehouse_list: for wh in warehouse_list:
wh_doc = frappe.get_doc("Warehouse", wh.name) wh_doc = frappe.get_doc("Warehouse", wh.name)
wh_doc.flags.ignore_permissions = True
wh_doc.save() wh_doc.save()

View File

@ -8,7 +8,7 @@
"description": "Track separate Income and Expense for product verticals or divisions.", "description": "Track separate Income and Expense for product verticals or divisions.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -298,7 +298,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:55.020690",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Cost Center", "name": "Cost Center",

View File

@ -8,7 +8,7 @@
"description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.", "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -133,7 +133,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:56.280252",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Fiscal Year", "name": "Fiscal Year",

View File

@ -6,7 +6,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -39,7 +39,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-10-02 13:35:44.155278", "modified": "2015-09-14 02:55:56.368682",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Fiscal Year Company", "name": "Fiscal Year Company",

View File

@ -156,7 +156,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Debit Amt", "label": "Debit Amount",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "debit", "oldfieldname": "debit",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
@ -180,7 +180,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Credit Amt", "label": "Credit Amount",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "credit", "oldfieldname": "credit",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
@ -194,6 +194,75 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Currency",
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "debit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Debit Amount in Account Currency",
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "credit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Credit Amount in Account Currency",
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -442,7 +511,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 15:51:26",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "GL Entry", "name": "GL Entry",

View File

@ -10,6 +10,8 @@ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class CustomerFrozen(frappe.ValidationError): pass class CustomerFrozen(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
class GLEntry(Document): class GLEntry(Document):
def validate(self): def validate(self):
@ -20,6 +22,7 @@ class GLEntry(Document):
self.check_pl_account() self.check_pl_account()
self.validate_cost_center() self.validate_cost_center()
self.validate_party() self.validate_party()
self.validate_currency()
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'): def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
self.validate_account_details(adv_adj) self.validate_account_details(adv_adj)
@ -99,6 +102,25 @@ class GLEntry(Document):
if frappe.db.get_value(self.party_type, self.party, "is_frozen"): if frappe.db.get_value(self.party_type, self.party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen) frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen)
def validate_currency(self):
company_currency = frappe.db.get_value("Company", self.company, "default_currency")
account_currency = frappe.db.get_value("Account", self.account, "account_currency") or company_currency
if not self.account_currency:
self.account_currency = company_currency
if account_currency != self.account_currency:
frappe.throw(_("Accounting Entry for {0} can only be made in currency: {1}")
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
if self.party_type and self.party:
party_account_currency = frappe.db.get_value(self.party_type, self.party, "party_account_currency") \
or company_currency
if party_account_currency != self.account_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(self.party_type, self.party, party_account_currency), InvalidAccountCurrency)
def validate_balance_type(account, adv_adj=False): def validate_balance_type(account, adv_adj=False):
if not adv_adj and account: if not adv_adj and account:
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be") balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
@ -131,7 +153,8 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
party_condition = "" party_condition = ""
# get final outstanding amt # get final outstanding amt
bal = flt(frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) bal = flt(frappe.db.sql("""
select sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))
from `tabGL Entry` from `tabGL Entry`
where against_voucher_type=%s and against_voucher=%s where against_voucher_type=%s and against_voucher=%s
and account = %s {0}""".format(party_condition), and account = %s {0}""".format(party_condition),
@ -141,7 +164,7 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
bal = -bal bal = -bal
elif against_voucher_type == "Journal Entry": elif against_voucher_type == "Journal Entry":
against_voucher_amount = flt(frappe.db.sql(""" against_voucher_amount = flt(frappe.db.sql("""
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) select sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))
from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
and account = %s and ifnull(against_voucher, '') = '' {0}""" and account = %s and ifnull(against_voucher, '') = '' {0}"""
.format(party_condition), (against_voucher, account))[0][0]) .format(party_condition), (against_voucher, account))[0][0])

View File

@ -11,7 +11,7 @@ class TestGLEntry(unittest.TestCase):
frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC") frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 100, "_Test Cost Center - _TC", submit=False) "_Test Bank - _TC", 100, "_Test Cost Center - _TC", submit=False)
jv.get("accounts")[0].debit = 100.01 jv.get("accounts")[0].debit = 100.01
jv.flags.ignore_validate = True jv.flags.ignore_validate = True

View File

@ -2,8 +2,54 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.accounts"); frappe.provide("erpnext.accounts");
frappe.provide("erpnext.journal_entry");
frappe.require("assets/erpnext/js/utils.js"); frappe.require("assets/erpnext/js/utils.js");
frappe.ui.form.on("Journal Entry", {
refresh: function(frm) {
erpnext.toggle_naming_series();
cur_frm.cscript.voucher_type(frm.doc);
if(frm.doc.docstatus==1) {
cur_frm.add_custom_button(__('View Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.posting_date,
"to_date": frm.doc.posting_date,
"company": frm.doc.company,
group_by_voucher: 0
};
frappe.set_route("query-report", "General Ledger");
}, "icon-table");
}
// hide /unhide fields based on currency
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
},
multi_currency: function(frm) {
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
}
})
erpnext.journal_entry.toggle_fields_based_on_currency = function(frm) {
var fields = ["currency_section", "account_currency", "exchange_rate", "debit", "credit"];
var grid = frm.get_field("accounts").grid;
if(grid) grid.set_column_disp(fields, frm.doc.multi_currency);
// dynamic label
var field_label_map = {
"debit_in_account_currency": "Debit",
"credit_in_account_currency": "Credit"
};
$.each(field_label_map, function (fieldname, label) {
var df = frappe.meta.get_docfield("Journal Entry Account", fieldname, frm.doc.name);
df.label = frm.doc.multi_currency ? (label + " in Account Currency") : label;
})
}
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
onload: function() { onload: function() {
this.load_defaults(); this.load_defaults();
@ -30,9 +76,20 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
setup_queries: function() { setup_queries: function() {
var me = this; var me = this;
$.each(["account", "cost_center"], function(i, fieldname) { me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
me.frm.set_query(fieldname, "accounts", function() { var filters = {
frappe.model.validate_missing(me.frm.doc, "company"); company: me.frm.doc.company,
is_group: 0
};
if(!doc.multi_currency) {
$.extend(filters, {
account_currency: frappe.get_doc(":Company", me.frm.doc.company).default_currency
});
}
return { filters: filters };
});
me.frm.set_query("cost_center", "accounts", function(doc, cdt, cdn) {
return { return {
filters: { filters: {
company: me.frm.doc.company, company: me.frm.doc.company,
@ -40,7 +97,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
} }
}; };
}); });
});
me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) { me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
return { return {
@ -118,32 +174,39 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
reference_name: function(doc, cdt, cdn) { reference_name: function(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn); var d = frappe.get_doc(cdt, cdn);
if(d.reference_name) {
if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) { if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
this.get_outstanding('Purchase Invoice', d.reference_name, d); this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d);
} }
if (d.reference_type==="Sales Invoice" && !flt(d.credit)) { if (d.reference_type==="Sales Invoice" && !flt(d.credit)) {
this.get_outstanding('Sales Invoice', d.reference_name, d); this.get_outstanding('Sales Invoice', d.reference_name, doc.company, d);
} }
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) { if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
this.get_outstanding('Journal Entry', d.reference_name, d); this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
}
} }
}, },
get_outstanding: function(doctype, docname, child) { get_outstanding: function(doctype, docname, company, child) {
var me = this; var me = this;
var args = { var args = {
"doctype": doctype, "doctype": doctype,
"docname": docname, "docname": docname,
"party": child.party, "party": child.party,
"account": child.account "account": child.account,
"account_currency": child.account_currency,
"company": company
} }
return this.frm.call({ return frappe.call({
child: child, method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_outstanding",
method: "get_outstanding",
args: { args: args}, args: { args: args},
callback: function(r) { callback: function(r) {
cur_frm.cscript.update_totals(me.frm.doc); if(r.message) {
$.each(r.message, function(field, value) {
frappe.model.set_value(child.doctype, child.name, field, value);
})
}
} }
}); });
}, },
@ -161,35 +224,20 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// set difference // set difference
if(doc.difference) { if(doc.difference) {
if(doc.difference > 0) { if(doc.difference > 0) {
row.credit_in_account_currency = doc.difference;
row.credit = doc.difference; row.credit = doc.difference;
} else { } else {
row.debit_in_account_currency = -doc.difference;
row.debit = -doc.difference; row.debit = -doc.difference;
} }
} }
cur_frm.cscript.update_totals(doc);
}, },
}); });
cur_frm.script_manager.make(erpnext.accounts.JournalEntry); cur_frm.script_manager.make(erpnext.accounts.JournalEntry);
cur_frm.cscript.refresh = function(doc) {
erpnext.toggle_naming_series();
cur_frm.cscript.voucher_type(doc);
if(doc.docstatus==1) {
cur_frm.add_custom_button(__('View Ledger'), function() {
frappe.route_options = {
"voucher_no": doc.name,
"from_date": doc.posting_date,
"to_date": doc.posting_date,
"company": doc.company,
group_by_voucher: 0
};
frappe.set_route("query-report", "General Ledger");
}, "icon-table");
}
}
cur_frm.cscript.company = function(doc, cdt, cdn) { cur_frm.cscript.company = function(doc, cdt, cdn) {
cur_frm.refresh_fields(); cur_frm.refresh_fields();
erpnext.get_fiscal_year(doc.company, doc.posting_date); erpnext.get_fiscal_year(doc.company, doc.posting_date);
@ -201,10 +249,10 @@ cur_frm.cscript.posting_date = function(doc, cdt, cdn){
cur_frm.cscript.update_totals = function(doc) { cur_frm.cscript.update_totals = function(doc) {
var td=0.0; var tc =0.0; var td=0.0; var tc =0.0;
var el = doc.accounts || []; var accounts = doc.accounts || [];
for(var i in el) { for(var i in accounts) {
td += flt(el[i].debit, precision("debit", el[i])); td += flt(accounts[i].debit, precision("debit", accounts[i]));
tc += flt(el[i].credit, precision("credit", el[i])); tc += flt(accounts[i].credit, precision("credit", accounts[i]));
} }
var doc = locals[doc.doctype][doc.name]; var doc = locals[doc.doctype][doc.name];
doc.total_debit = td; doc.total_debit = td;
@ -213,32 +261,12 @@ cur_frm.cscript.update_totals = function(doc) {
refresh_many(['total_debit','total_credit','difference']); refresh_many(['total_debit','total_credit','difference']);
} }
cur_frm.cscript.debit = function(doc,dt,dn) { cur_frm.cscript.update_totals(doc); }
cur_frm.cscript.credit = function(doc,dt,dn) { cur_frm.cscript.update_totals(doc); }
cur_frm.cscript.get_balance = function(doc,dt,dn) { cur_frm.cscript.get_balance = function(doc,dt,dn) {
cur_frm.cscript.update_totals(doc); cur_frm.cscript.update_totals(doc);
return $c_obj(cur_frm.doc, 'get_balance', '', function(r, rt){ return $c_obj(cur_frm.doc, 'get_balance', '', function(r, rt){
cur_frm.refresh(); cur_frm.refresh();
}); });
} }
// Get balance
// -----------
cur_frm.cscript.account = function(doc,dt,dn) {
var d = locals[dt][dn];
if(d.account) {
return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
args: {account: d.account, date: doc.posting_date},
callback: function(r) {
$.extend(d, r.message);
refresh_field('balance', d.name, 'accounts');
refresh_field('party_type', d.name, 'accounts');
}
});
}
}
cur_frm.cscript.validate = function(doc,cdt,cdn) { cur_frm.cscript.validate = function(doc,cdt,cdn) {
cur_frm.cscript.update_totals(doc); cur_frm.cscript.update_totals(doc);
@ -303,7 +331,8 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
} }
} }
frappe.ui.form.on("Journal Entry Account", "party", function(frm, cdt, cdn) { frappe.ui.form.on("Journal Entry Account", {
party: function(frm, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn); var d = frappe.get_doc(cdt, cdn);
if(!d.account && d.party_type && d.party) { if(!d.account && d.party_type && d.party) {
return frm.call({ return frm.call({
@ -316,9 +345,91 @@ frappe.ui.form.on("Journal Entry Account", "party", function(frm, cdt, cdn) {
} }
}); });
} }
},
account: function(frm, dt, dn) {
var d = locals[dt][dn];
if(d.account) {
return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
args: {
account: d.account,
date: frm.doc.posting_date,
company: frm.doc.company,
debit: flt(d.debit_in_account_currency),
credit: flt(d.credit_in_account_currency),
exchange_rate: d.exchange_rate
},
callback: function(r) {
if(r.message) {
$.extend(d, r.message);
refresh_field('accounts');
}
}
});
}
},
debit_in_account_currency: function(frm, cdt, cdn) {
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
},
credit_in_account_currency: function(frm, cdt, cdn) {
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
},
debit: function(frm, dt, dn) {
cur_frm.cscript.update_totals(frm.doc);
},
credit: function(frm, dt, dn) {
cur_frm.cscript.update_totals(frm.doc);
},
exchange_rate: function(frm, cdt, cdn) {
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
}
}) })
frappe.ui.form.on("Journal Entry Account", "accounts_remove", function(frm) { frappe.ui.form.on("Journal Entry Account", "accounts_remove", function(frm) {
cur_frm.cscript.update_totals(frm.doc); cur_frm.cscript.update_totals(frm.doc);
}); });
erpnext.journal_entry.set_debit_credit_in_company_currency = function(frm, cdt, cdn) {
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
var row = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, "debit",
flt(flt(row.debit_in_account_currency)*row.exchange_rate), precision("debit", row));
frappe.model.set_value(cdt, cdn, "credit",
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
}
erpnext.journal_entry.set_exchange_rate = function(frm, cdt, cdn) {
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
var row = locals[cdt][cdn];
if(row.account_currency == company_currency || !frm.doc.multi_currency) {
frappe.model.set_value(cdt, cdn, "exchange_rate", 1);
} else if (!row.exchange_rate || row.account_type == "Bank") {
frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
args: {
account: row.account,
account_currency: row.account_currency,
company: frm.doc.company,
reference_type: cstr(row.reference_type),
reference_name: cstr(row.reference_name),
debit: flt(row.debit_in_account_currency),
credit: flt(row.credit_in_account_currency),
exchange_rate: row.exchange_rate
},
callback: function(r) {
if(r.message) {
frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
}
}
})
}
}

View File

@ -42,7 +42,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -400,6 +400,28 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "multi_currency",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Multi Currency",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1002,7 +1024,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:21:50.635624",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@ -3,11 +3,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, date_diff from frappe.utils import cstr, flt, fmt_money, formatdate
from frappe import msgprint, _, scrub from frappe import msgprint, _, scrub
from erpnext.setup.utils import get_company_currency
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on
from erpnext.setup.utils import get_company_currency
class JournalEntry(AccountsController): class JournalEntry(AccountsController):
@ -26,6 +26,7 @@ class JournalEntry(AccountsController):
self.validate_party() self.validate_party()
self.validate_cheque_info() self.validate_cheque_info()
self.validate_entries_for_advance() self.validate_entries_for_advance()
self.validate_multi_currency()
self.validate_debit_and_credit() self.validate_debit_and_credit()
self.validate_against_jv() self.validate_against_jv()
self.validate_reference_doc() self.validate_reference_doc()
@ -35,6 +36,7 @@ class JournalEntry(AccountsController):
self.validate_expense_claim() self.validate_expense_claim()
self.validate_credit_debit_note() self.validate_credit_debit_note()
self.validate_empty_accounts_table() self.validate_empty_accounts_table()
self.set_account_and_party_balance()
self.set_title() self.set_title()
def on_submit(self): def on_submit(self):
@ -144,6 +146,7 @@ class JournalEntry(AccountsController):
self.reference_totals = {} self.reference_totals = {}
self.reference_types = {} self.reference_types = {}
self.reference_parties = {}
for d in self.get("accounts"): for d in self.get("accounts"):
if not d.reference_type: if not d.reference_type:
@ -151,8 +154,8 @@ class JournalEntry(AccountsController):
if not d.reference_name: if not d.reference_name:
d.reference_type = None d.reference_type = None
if d.reference_type and d.reference_name and (d.reference_type in field_dict.keys()): if d.reference_type and d.reference_name and (d.reference_type in field_dict.keys()):
dr_or_cr = "credit" if d.reference_type in ("Sales Order", "Sales Invoice") \ dr_or_cr = "credit_in_account_currency" \
else "debit" if d.reference_type in ("Sales Order", "Sales Invoice") else "debit_in_account_currency"
# check debit or credit type Sales / Purchase Order # check debit or credit type Sales / Purchase Order
if d.reference_type=="Sales Order" and flt(d.debit) > 0: if d.reference_type=="Sales Order" and flt(d.debit) > 0:
@ -166,6 +169,8 @@ class JournalEntry(AccountsController):
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)) 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
if d.party_type and d.party:
self.reference_parties[d.reference_name] = [d.party_type, d.party]
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name, against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
[scrub(dt) for dt in field_dict.get(d.reference_type)]) [scrub(dt) for dt in field_dict.get(d.reference_type)])
@ -191,23 +196,31 @@ class JournalEntry(AccountsController):
"""Validate totals, stopped and docstatus for orders""" """Validate totals, stopped and docstatus for orders"""
for reference_name, total in self.reference_totals.iteritems(): for reference_name, total in self.reference_totals.iteritems():
reference_type = self.reference_types[reference_name] reference_type = self.reference_types[reference_name]
party_type, party = self.reference_parties.get(reference_name)
if reference_type in ("Sales Order", "Purchase Order"): if reference_type in ("Sales Order", "Purchase Order"):
voucher_properties = frappe.db.get_value(reference_type, reference_name, order = frappe.db.get_value(reference_type, reference_name,
["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"]) ["docstatus", "per_billed", "status", "advance_paid",
"base_grand_total", "grand_total", "currency"], as_dict=1)
if voucher_properties[0] != 1: if order.docstatus != 1:
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
if flt(voucher_properties[1]) >= 100: if flt(order.per_billed) >= 100:
frappe.throw(_("{0} {1} is fully billed").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is fully billed").format(reference_type, reference_name))
if cstr(voucher_properties[2]) == "Stopped": if cstr(order.status) == "Stopped":
frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name))
if flt(voucher_properties[4]) < (flt(voucher_properties[3]) + total): party_account_currency = frappe.db.get_value(party_type, party, "party_account_currency")
if party_account_currency == self.company_currency:
voucher_total = order.base_grand_total
else:
voucher_total = order.grand_total
if flt(voucher_total) < (flt(order.advance_paid) + total):
frappe.throw(_("Advance paid against {0} {1} cannot be greater \ frappe.throw(_("Advance paid against {0} {1} cannot be greater \
than Grand Total {2}").format(reference_type, reference_name, voucher_properties[4])) than Grand Total {2}").format(reference_type, reference_name, voucher_total))
def validate_invoices(self): def validate_invoices(self):
"""Validate totals and docstatus for invoices""" """Validate totals and docstatus for invoices"""
@ -215,15 +228,15 @@ class JournalEntry(AccountsController):
reference_type = self.reference_types[reference_name] reference_type = self.reference_types[reference_name]
if reference_type in ("Sales Invoice", "Purchase Invoice"): if reference_type in ("Sales Invoice", "Purchase Invoice"):
voucher_properties = frappe.db.get_value(reference_type, reference_name, invoice = frappe.db.get_value(reference_type, reference_name,
["docstatus", "outstanding_amount"]) ["docstatus", "outstanding_amount"], as_dict=1)
if voucher_properties[0] != 1: if invoice.docstatus != 1:
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
if total and flt(voucher_properties[1]) < total: if total and flt(invoice.outstanding_amount) < total:
frappe.throw(_("Payment against {0} {1} cannot be greater \ frappe.throw(_("Payment against {0} {1} cannot be greater than Outstanding Amount {2}")
than Outstanding Amount {2}").format(reference_type, reference_name, voucher_properties[1])) .format(reference_type, reference_name, invoice.outstanding_amount))
def set_against_account(self): def set_against_account(self):
accounts_debited, accounts_credited = [], [] accounts_debited, accounts_credited = [], []
@ -237,13 +250,12 @@ class JournalEntry(AccountsController):
def validate_debit_and_credit(self): def validate_debit_and_credit(self):
self.total_debit, self.total_credit, self.difference = 0, 0, 0 self.total_debit, self.total_credit, self.difference = 0, 0, 0
for d in self.get("accounts"): for d in self.get("accounts"):
if d.debit and d.credit: if d.debit and d.credit:
frappe.throw(_("You cannot credit and debit same account at the same time")) frappe.throw(_("You cannot credit and debit same account at the same time"))
self.total_debit = flt(self.total_debit) + flt(d.debit, self.precision("debit", "accounts")) self.total_debit = flt(self.total_debit) + flt(d.debit, d.precision("debit"))
self.total_credit = flt(self.total_credit) + flt(d.credit, self.precision("credit", "accounts")) self.total_credit = flt(self.total_credit) + flt(d.credit, d.precision("credit"))
self.difference = flt(self.total_debit, self.precision("total_debit")) - \ self.difference = flt(self.total_debit, self.precision("total_debit")) - \
flt(self.total_credit, self.precision("total_credit")) flt(self.total_credit, self.precision("total_credit"))
@ -252,6 +264,41 @@ class JournalEntry(AccountsController):
frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}") frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}")
.format(self.difference)) .format(self.difference))
def validate_multi_currency(self):
alternate_currency = []
for d in self.get("accounts"):
account = frappe.db.get_value("Account", d.account, ["account_currency", "account_type"], as_dict=1)
d.account_currency = account.account_currency or self.company_currency
d.account_type = account.account_type
if d.account_currency!=self.company_currency and d.account_currency not in alternate_currency:
alternate_currency.append(d.account_currency)
if alternate_currency:
if not self.multi_currency:
frappe.throw(_("Please check Multi Currency option to allow accounts with other currency"))
if len(alternate_currency) > 1:
frappe.throw(_("Only one alternate currency can be used in a single Journal Entry"))
self.set_exchange_rate()
for d in self.get("accounts"):
d.debit = flt(flt(d.debit_in_account_currency)*flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(flt(d.credit_in_account_currency)*flt(d.exchange_rate), d.precision("credit"))
def set_exchange_rate(self):
for d in self.get("accounts"):
if d.account_currency == self.company_currency:
d.exchange_rate = 1
elif not d.exchange_rate or d.account_type=="Bank" or \
(d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name):
d.exchange_rate = get_exchange_rate(d.account, d.account_currency, self.company,
d.reference_type, d.reference_name, d.debit, d.credit, d.exchange_rate)
if not d.exchange_rate:
frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
def create_remarks(self): def create_remarks(self):
r = [] r = []
if self.cheque_no: if self.cheque_no:
@ -260,15 +307,13 @@ class JournalEntry(AccountsController):
else: else:
msgprint(_("Please enter Reference date"), raise_exception=frappe.MandatoryError) msgprint(_("Please enter Reference date"), raise_exception=frappe.MandatoryError)
company_currency = get_company_currency(self.company)
for d in self.get('accounts'): for d in self.get('accounts'):
if d.reference_type=="Sales Invoice" and d.credit: if d.reference_type=="Sales Invoice" and d.credit:
r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
d.reference_name)) d.reference_name))
if d.reference_type=="Sales Order" and d.credit: if d.reference_type=="Sales Order" and d.credit:
r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
d.reference_name)) d.reference_name))
if d.reference_type == "Purchase Invoice" and d.debit: if d.reference_type == "Purchase Invoice" and d.debit:
@ -276,11 +321,11 @@ class JournalEntry(AccountsController):
from `tabPurchase Invoice` where name=%s""", d.reference_name) from `tabPurchase Invoice` where name=%s""", d.reference_name)
if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \ if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
not in ['na', 'not applicable', 'none']: not in ['na', 'not applicable', 'none']:
r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=company_currency), bill_no[0][0], r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=self.company_currency), bill_no[0][0],
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d')))) bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
if d.reference_type == "Purchase Order" and d.debit: if d.reference_type == "Purchase Order" and d.debit:
r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
d.reference_name)) d.reference_name))
if self.user_remark: if self.user_remark:
@ -301,10 +346,9 @@ class JournalEntry(AccountsController):
self.set_total_amount(d.debit or d.credit) self.set_total_amount(d.debit or d.credit)
def set_total_amount(self, amt): def set_total_amount(self, amt):
company_currency = get_company_currency(self.company)
self.total_amount = amt self.total_amount = amt
from frappe.utils import money_in_words from frappe.utils import money_in_words
self.total_amount_in_words = money_in_words(amt, company_currency) self.total_amount_in_words = money_in_words(amt, self.company_currency)
def make_gl_entries(self, cancel=0, adv_adj=0): def make_gl_entries(self, cancel=0, adv_adj=0):
from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.general_ledger import make_gl_entries
@ -318,8 +362,11 @@ class JournalEntry(AccountsController):
"party_type": d.party_type, "party_type": d.party_type,
"party": d.party, "party": d.party,
"against": d.against_account, "against": d.against_account,
"debit": flt(d.debit, self.precision("debit", "accounts")), "debit": flt(d.debit, d.precision("debit")),
"credit": flt(d.credit, self.precision("credit", "accounts")), "credit": flt(d.credit, d.precision("credit")),
"account_currency": d.account_currency,
"debit_in_account_currency": flt(d.debit_in_account_currency, d.precision("debit_in_account_currency")),
"credit_in_account_currency": flt(d.credit_in_account_currency, d.precision("credit_in_account_currency")),
"against_voucher_type": d.reference_type, "against_voucher_type": d.reference_type,
"against_voucher": d.reference_name, "against_voucher": d.reference_name,
"remarks": self.remark, "remarks": self.remark,
@ -338,21 +385,21 @@ class JournalEntry(AccountsController):
diff = flt(self.difference, self.precision("difference")) diff = flt(self.difference, self.precision("difference"))
# If any row without amount, set the diff on that row # If any row without amount, set the diff on that row
if diff:
for d in self.get('accounts'): for d in self.get('accounts'):
if not d.credit and not d.debit and diff != 0: if not d.credit_in_account_currency and not d.debit_in_account_currency and diff != 0:
if diff>0: blank_row = d
d.credit = diff
elif diff<0:
d.debit = diff
flag = 1
# Set the diff in a new row if not blank_row:
if flag == 0 and diff != 0: blank_row = self.append('accounts', {})
jd = self.append('accounts', {})
blank_row.exchange_rate = 1
if diff>0: if diff>0:
jd.credit = abs(diff) blank_row.credit_in_account_currency = diff
blank_row.credit = diff
elif diff<0: elif diff<0:
jd.debit = abs(diff) blank_row.debit_in_account_currency = abs(diff)
blank_row.debit = abs(diff)
self.validate_debit_and_credit() self.validate_debit_and_credit()
@ -427,6 +474,11 @@ class JournalEntry(AccountsController):
if not self.get('accounts'): if not self.get('accounts'):
frappe.throw("Accounts table cannot be blank.") frappe.throw("Accounts table cannot be blank.")
def set_account_and_party_balance(self):
for d in self.get("accounts"):
d.account_balance = get_balance_on(account=d.account, date=self.posting_date)
d.party_balance = get_balance_on(party_type=d.party_type, party=d.party, date=self.posting_date)
@frappe.whitelist() @frappe.whitelist()
def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None): def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
@ -446,9 +498,12 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
account = frappe.db.get_value("Account", {"company": company, "account_type": "Cash", "is_group": 0}) account = frappe.db.get_value("Account", {"company": company, "account_type": "Cash", "is_group": 0})
if account: if account:
account_details = frappe.db.get_value("Account", account, ["account_currency", "account_type"], as_dict=1)
return { return {
"account": account, "account": account,
"balance": get_balance_on(account) "balance": get_balance_on(account),
"account_currency": account_details.account_currency,
"account_type": account_details.account_type
} }
@frappe.whitelist() @frappe.whitelist()
@ -456,21 +511,38 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
"""Returns new Journal Entry document as dict for given Sales Invoice""" """Returns new Journal Entry document as dict for given Sales Invoice"""
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on
si = frappe.get_doc("Sales Invoice", sales_invoice) si = frappe.get_doc("Sales Invoice", sales_invoice)
# exchange rate
exchange_rate = get_exchange_rate(si.debit_to, si.party_account_currency, si.company,
si.doctype, si.name)
jv = get_payment_entry(si) jv = get_payment_entry(si)
jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks) jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks)
# credit customer # credit customer
jv.get("accounts")[0].account = si.debit_to row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Customer" row1.account = si.debit_to
jv.get("accounts")[0].party = si.customer row1.account_currency = si.party_account_currency
jv.get("accounts")[0].balance = get_balance_on(si.debit_to) row1.party_type = "Customer"
jv.get("accounts")[0].party_balance = get_balance_on(party=si.customer, party_type="Customer") row1.party = si.customer
jv.get("accounts")[0].credit = si.outstanding_amount row1.balance = get_balance_on(si.debit_to)
jv.get("accounts")[0].reference_type = si.doctype row1.party_balance = get_balance_on(party=si.customer, party_type="Customer")
jv.get("accounts")[0].reference_name = si.name row1.credit_in_account_currency = si.outstanding_amount
row1.reference_type = si.doctype
row1.reference_name = si.name
row1.exchange_rate = exchange_rate
row1.account_type = "Receivable" if si.customer else ""
# debit bank # debit bank
jv.get("accounts")[1].debit = si.outstanding_amount row2 = jv.get("accounts")[1]
if row2.account_currency == si.party_account_currency:
row2.debit_in_account_currency = si.outstanding_amount
else:
row2.debit_in_account_currency = si.outstanding_amount * exchange_rate
# set multi currency check
if row1.account_currency != si.company_currency or row2.account_currency != si.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@ -478,21 +550,38 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
def get_payment_entry_from_purchase_invoice(purchase_invoice): def get_payment_entry_from_purchase_invoice(purchase_invoice):
"""Returns new Journal Entry document as dict for given Purchase Invoice""" """Returns new Journal Entry document as dict for given Purchase Invoice"""
pi = frappe.get_doc("Purchase Invoice", purchase_invoice) pi = frappe.get_doc("Purchase Invoice", purchase_invoice)
exchange_rate = get_exchange_rate(pi.debit_to, pi.party_account_currency, pi.company,
pi.doctype, pi.name)
jv = get_payment_entry(pi) jv = get_payment_entry(pi)
jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks) jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks)
jv.exchange_rate = exchange_rate
# credit supplier # credit supplier
jv.get("accounts")[0].account = pi.credit_to row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Supplier" row1.account = pi.credit_to
jv.get("accounts")[0].party = pi.supplier row1.account_currency = pi.party_account_currency
jv.get("accounts")[0].balance = get_balance_on(pi.credit_to) row1.party_type = "Supplier"
jv.get("accounts")[0].party_balance = get_balance_on(party=pi.supplier, party_type="Supplier") row1.party = pi.supplier
jv.get("accounts")[0].debit = pi.outstanding_amount row1.balance = get_balance_on(pi.credit_to)
jv.get("accounts")[0].reference_type = pi.doctype row1.party_balance = get_balance_on(party=pi.supplier, party_type="Supplier")
jv.get("accounts")[0].reference_name = pi.name row1.debit_in_account_currency = pi.outstanding_amount
row1.reference_type = pi.doctype
row1.reference_name = pi.name
row1.exchange_rate = exchange_rate
row1.account_type = "Payable" if pi.supplier else ""
# credit bank # credit bank
jv.get("accounts")[1].credit = pi.outstanding_amount row2 = jv.get("accounts")[1]
if row2.account_currency == pi.party_account_currency:
row2.credit_in_account_currency = pi.outstanding_amount
else:
row2.credit_in_account_currency = pi.outstanding_amount * exchange_rate
# set multi currency check
if row1.account_currency != pi.company_currency or row2.account_currency != pi.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@ -501,6 +590,7 @@ def get_payment_entry_from_sales_order(sales_order):
"""Returns new Journal Entry document as dict for given Sales Order""" """Returns new Journal Entry document as dict for given Sales Order"""
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
so = frappe.get_doc("Sales Order", sales_order) so = frappe.get_doc("Sales Order", sales_order)
if flt(so.per_billed, 2) != 0.0: if flt(so.per_billed, 2) != 0.0:
@ -508,23 +598,42 @@ def get_payment_entry_from_sales_order(sales_order):
jv = get_payment_entry(so) jv = get_payment_entry(so)
jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name) jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name)
party_account = get_party_account(so.company, so.customer, "Customer")
party_account = get_party_account(so.company, so.customer, "Customer")
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company)
if party_account_currency == so.company_currency:
amount = flt(so.base_grand_total) - flt(so.advance_paid) amount = flt(so.base_grand_total) - flt(so.advance_paid)
else:
amount = flt(so.grand_total) - flt(so.advance_paid)
# credit customer # credit customer
jv.get("accounts")[0].account = party_account row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Customer" row1.account = party_account
jv.get("accounts")[0].party = so.customer row1.account_currency = party_account_currency
jv.get("accounts")[0].balance = get_balance_on(party_account) row1.party_type = "Customer"
jv.get("accounts")[0].party_balance = get_balance_on(party=so.customer, party_type="Customer") row1.party = so.customer
jv.get("accounts")[0].credit = amount row1.balance = get_balance_on(party_account)
jv.get("accounts")[0].reference_type = so.doctype row1.party_balance = get_balance_on(party=so.customer, party_type="Customer")
jv.get("accounts")[0].reference_name = so.name row1.credit_in_account_currency = amount
jv.get("accounts")[0].is_advance = "Yes" row1.reference_type = so.doctype
row1.reference_name = so.name
row1.is_advance = "Yes"
row1.exchange_rate = exchange_rate
row1.account_type = "Receivable"
# debit bank # debit bank
jv.get("accounts")[1].debit = amount row2 = jv.get("accounts")[1]
if row2.account_currency == party_account_currency:
row2.debit_in_account_currency = amount
else:
row2.debit_in_account_currency = amount * exchange_rate
# set multi currency check
if row1.account_currency != so.company_currency or row2.account_currency != so.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@ -540,23 +649,41 @@ def get_payment_entry_from_purchase_order(purchase_order):
jv = get_payment_entry(po) jv = get_payment_entry(po)
jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name) jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name)
party_account = get_party_account(po.company, po.supplier, "Supplier")
party_account = get_party_account(po.company, po.supplier, "Supplier")
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
exchange_rate = get_exchange_rate(party_account, party_account_currency, po.company)
if party_account_currency == po.company_currency:
amount = flt(po.base_grand_total) - flt(po.advance_paid) amount = flt(po.base_grand_total) - flt(po.advance_paid)
else:
amount = flt(po.grand_total) - flt(po.advance_paid)
# credit customer # credit customer
jv.get("accounts")[0].account = party_account row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Supplier" row1.account = party_account
jv.get("accounts")[0].party = po.supplier row1.party_type = "Supplier"
jv.get("accounts")[0].balance = get_balance_on(party_account) row1.party = po.supplier
jv.get("accounts")[0].party_balance = get_balance_on(party=po.supplier, party_type="Supplier") row1.balance = get_balance_on(party_account)
jv.get("accounts")[0].debit = amount row1.party_balance = get_balance_on(party=po.supplier, party_type="Supplier")
jv.get("accounts")[0].reference_type = po.doctype row1.debit_in_account_currency = amount
jv.get("accounts")[0].reference_name = po.name row1.reference_type = po.doctype
jv.get("accounts")[0].is_advance = "Yes" row1.reference_name = po.name
row1.is_advance = "Yes"
row1.exchange_rate = exchange_rate
row1.account_type = "Payable"
# debit bank # debit bank
jv.get("accounts")[1].credit = amount row2 = jv.get("accounts")[1]
if row2.account_currency == party_account_currency:
row2.credit_in_account_currency = amount
else:
row2.credit_in_account_currency = amount * exchange_rate
# set multi currency check
if row1.account_currency != po.company_currency or row2.account_currency != po.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@ -574,6 +701,10 @@ def get_payment_entry(doc):
if bank_account: if bank_account:
d2.account = bank_account["account"] d2.account = bank_account["account"]
d2.balance = bank_account["balance"] d2.balance = bank_account["balance"]
d2.account_currency = bank_account["account_currency"]
d2.account_type = bank_account["account_type"]
d2.exchange_rate = get_exchange_rate(bank_account["account"],
bank_account["account_currency"], doc.company)
return jv return jv
@ -599,27 +730,37 @@ def get_outstanding(args):
if not frappe.has_permission("Account"): if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
args = eval(args) args = eval(args)
company_currency = get_company_currency(args.get("company"))
if args.get("doctype") == "Journal Entry": if args.get("doctype") == "Journal Entry":
condition = " and party=%(party)s" if args.get("party") else "" condition = " and party=%(party)s" if args.get("party") else ""
against_jv_amount = frappe.db.sql(""" against_jv_amount = frappe.db.sql("""
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) select sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0} from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
and ifnull(reference_type, '')=''""".format(condition), args) and ifnull(reference_type, '')=''""".format(condition), args)
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0 against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
amount_field = "credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
return { return {
("credit" if against_jv_amount > 0 else "debit"): abs(against_jv_amount) amount_field: abs(against_jv_amount)
} }
elif args.get("doctype") == "Sales Invoice": elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", args["docname"], "outstanding_amount")) invoice = frappe.db.get_value(args["doctype"], args["docname"],
["outstanding_amount", "conversion_rate"], as_dict=1)
exchange_rate = invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
if args["doctype"] == "Sales Invoice":
amount_field = "credit_in_account_currency" \
if flt(invoice.outstanding_amount) > 0 else "debit_in_account_currency"
else:
amount_field = "debit_in_account_currency" \
if flt(invoice.outstanding_amount) > 0 else "credit_in_account_currency"
return { return {
("credit" if outstanding_amount > 0 else "debit"): abs(outstanding_amount) amount_field: abs(flt(invoice.outstanding_amount)),
} "exchange_rate": exchange_rate
elif args.get("doctype") == "Purchase Invoice":
outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", args["docname"], "outstanding_amount"))
return {
("debit" if outstanding_amount > 0 else "credit"): abs(outstanding_amount)
} }
@frappe.whitelist() @frappe.whitelist()
@ -640,14 +781,58 @@ def get_party_account_and_balance(company, party_type, party):
} }
@frappe.whitelist() @frappe.whitelist()
def get_account_balance_and_party_type(account, date): def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
"""Returns dict of account balance and party type to be set in Journal Entry on selection of account.""" """Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
if not frappe.has_permission("Account"): if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
account_type = frappe.db.get_value("Account", account, "account_type") company_currency = get_company_currency(company)
return { account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
"balance": get_balance_on(account, date),
"party_type": {"Receivable":"Customer", "Payable":"Supplier"}.get(account_type, "")
}
if account_details.account_type == "Receivable":
party_type = "Customer"
elif account_details.account_type == "Payable":
party_type = "Supplier"
else:
party_type = ""
grid_values = {
"balance": get_balance_on(account, date),
"party_type": party_type,
"account_type": account_details.account_type,
"account_currency": account_details.account_currency or company_currency,
"exchange_rate": get_exchange_rate(account, account_details.account_currency,
company, debit=debit, credit=credit, exchange_rate=exchange_rate)
}
return grid_values
@frappe.whitelist()
def get_exchange_rate(account, account_currency, company,
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
from erpnext.setup.utils import get_exchange_rate
company_currency = get_company_currency(company)
account_details = frappe.db.get_value("Account", account, ["account_type", "root_type"], as_dict=1)
if account_currency != company_currency:
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate")
elif account_details.account_type == "Bank" and \
((account_details.root_type == "Asset" and flt(credit) > 0) or
(account_details.root_type == "Liability" and debit)):
exchange_rate = get_average_exchange_rate(account)
if not exchange_rate:
exchange_rate = get_exchange_rate(account_currency, company_currency)
else:
exchange_rate = 1
return exchange_rate
def get_average_exchange_rate(account):
exchange_rate = 0
bank_balance_in_account_currency = get_balance_on(account)
if bank_balance_in_account_currency:
bank_balance_in_company_currency = get_balance_on(account, in_account_currency=False)
exchange_rate = bank_balance_in_company_currency / bank_balance_in_account_currency
return exchange_rate

View File

@ -101,7 +101,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 40000, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name})) {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
@ -112,7 +112,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 40000, "_Test Cost Center - _TC") "_Test Bank - _TC", 40000, "_Test Cost Center - _TC")
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@ -126,7 +126,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 150000, "_Test Cost Center - _TC") "_Test Bank - _TC", 150000, "_Test Cost Center - _TC")
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@ -136,13 +136,13 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 20000, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv1.name})) {"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 20000, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv2.name})) {"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
@ -165,28 +165,76 @@ class TestJournalEntry(unittest.TestCase):
def set_total_expense_zero(self, posting_date): def set_total_expense_zero(self, posting_date):
existing_expense = self.get_actual_expense(posting_date) existing_expense = self.get_actual_expense(posting_date)
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True)
def make_journal_entry(account1, account2, amount, cost_center=None, submit=False): def test_multi_currency(self):
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Bank - _TC", 100, exchange_rate=50, save=False)
jv.get("accounts")[1].credit_in_account_currency = 5000
jv.submit()
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
order by account asc""", jv.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = {
"_Test Bank USD - _TC": {
"account_currency": "USD",
"debit": 5000,
"debit_in_account_currency": 100,
"credit": 0,
"credit_in_account_currency": 0
},
"_Test Bank - _TC": {
"account_currency": "INR",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
"credit_in_account_currency": 5000
}
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[gle.account][field], gle[field])
# cancel
jv.cancel()
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", jv.name)
self.assertFalse(gle)
def make_journal_entry(account1, account2, amount, cost_center=None, exchange_rate=1, save=True, submit=False):
jv = frappe.new_doc("Journal Entry") jv = frappe.new_doc("Journal Entry")
jv.posting_date = "2013-02-14" jv.posting_date = "2013-02-14"
jv.company = "_Test Company" jv.company = "_Test Company"
jv.fiscal_year = "_Test Fiscal Year 2013" jv.fiscal_year = "_Test Fiscal Year 2013"
jv.user_remark = "test" jv.user_remark = "test"
jv.multi_currency = 1
jv.set("accounts", [ jv.set("accounts", [
{ {
"account": account1, "account": account1,
"cost_center": cost_center, "cost_center": cost_center,
"debit": amount if amount > 0 else 0, "debit_in_account_currency": amount if amount > 0 else 0,
"credit": abs(amount) if amount < 0 else 0, "credit_in_account_currency": abs(amount) if amount < 0 else 0,
"exchange_rate": exchange_rate
}, { }, {
"account": account2, "account": account2,
"cost_center": cost_center, "cost_center": cost_center,
"credit": amount if amount > 0 else 0, "credit_in_account_currency": amount if amount > 0 else 0,
"debit": abs(amount) if amount < 0 else 0, "debit_in_account_currency": abs(amount) if amount < 0 else 0,
exchange_rate: exchange_rate
} }
]) ])
if save or submit:
jv.insert() jv.insert()
if submit: if submit:

View File

@ -9,15 +9,15 @@
"account": "_Test Receivable - _TC", "account": "_Test Receivable - _TC",
"party_type": "Customer", "party_type": "Customer",
"party": "_Test Customer", "party": "_Test Customer",
"credit": 400.0, "credit_in_account_currency": 400.0,
"debit": 0.0, "debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
}, },
{ {
"account": "_Test Account Bank Account - _TC", "account": "_Test Bank - _TC",
"credit": 0.0, "credit_in_account_currency": 0.0,
"debit": 400.0, "debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
} }
@ -40,15 +40,15 @@
"account": "_Test Payable - _TC", "account": "_Test Payable - _TC",
"party_type": "Supplier", "party_type": "Supplier",
"party": "_Test Supplier", "party": "_Test Supplier",
"credit": 0.0, "credit_in_account_currency": 0.0,
"debit": 400.0, "debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
}, },
{ {
"account": "_Test Account Bank Account - _TC", "account": "_Test Bank - _TC",
"credit": 400.0, "credit_in_account_currency": 400.0,
"debit": 0.0, "debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
} }
@ -71,16 +71,16 @@
"account": "_Test Receivable - _TC", "account": "_Test Receivable - _TC",
"party_type": "Customer", "party_type": "Customer",
"party": "_Test Customer", "party": "_Test Customer",
"credit": 0.0, "credit_in_account_currency": 0.0,
"debit": 400.0, "debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
}, },
{ {
"account": "Sales - _TC", "account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"credit": 400.0, "credit_in_account_currency": 400.0,
"debit": 0.0, "debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
} }

View File

@ -34,6 +34,28 @@
"unique": 0, "unique": 0,
"width": "250px" "width": "250px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "account_type",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Type",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -48,7 +70,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "balance", "oldfieldname": "balance",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"options": "Company:company:default_currency", "options": "account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@ -162,7 +184,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Party Balance", "label": "Party Balance",
"no_copy": 0, "no_copy": 0,
"options": "Company:company:default_currency", "options": "account_currency",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -173,6 +195,96 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": "",
"depends_on": "",
"fieldname": "currency_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Currency",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "exchange_rate",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Exchange Rate",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -198,20 +310,43 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "debit", "fieldname": "debit_in_account_currency",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Debit", "label": "Debit in Account Currency",
"no_copy": 0, "no_copy": 0,
"options": "account_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "debit",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Debit in Company Currency",
"no_copy": 1,
"oldfieldname": "debit", "oldfieldname": "debit",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 1,
"read_only": 0, "read_only": 1,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -242,20 +377,43 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "credit", "fieldname": "credit_in_account_currency",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Credit", "label": "Credit in Account Currency",
"no_copy": 0, "no_copy": 0,
"options": "account_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "credit",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Credit in Company Currency",
"no_copy": 1,
"oldfieldname": "credit", "oldfieldname": "credit",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 1,
"read_only": 0, "read_only": 1,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -405,7 +563,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-17 02:11:33.991361", "modified": "2015-09-11 12:55:59.270539",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -65,7 +65,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:58.003800",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Mode of Payment", "name": "Mode of Payment",

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Mode of Payment')
class TestModeofPayment(unittest.TestCase):
pass

View File

@ -79,6 +79,7 @@ class PaymentReconciliation(Document):
`tabGL Entry` `tabGL Entry`
where where
party_type = %(party_type)s and party = %(party)s party_type = %(party_type)s and party = %(party)s
and voucher_type != "Journal Entry"
and account = %(account)s and {dr_or_cr} > 0 {cond} and account = %(account)s and {dr_or_cr} > 0 {cond}
group by voucher_type, voucher_no group by voucher_type, voucher_no
""".format(**{ """.format(**{

View File

@ -25,8 +25,14 @@ frappe.ui.form.on("Payment Tool", "onload", function(frm) {
}); });
frm.set_query("against_voucher_type", "vouchers", function() { frm.set_query("against_voucher_type", "vouchers", function() {
if (frm.doc.party_type=="Customer") {
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry"];
} else {
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
}
return { return {
filters: {"name": ["in", ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Sales Order", "Purchase Order"]]} filters: { "name": ["in", doctypes] }
}; };
}); });
}); });
@ -55,6 +61,25 @@ frappe.ui.form.on("Payment Tool", "party", function(frm) {
} }
}) })
frappe.ui.form.on("Payment Tool", "party_account", function(frm) {
if(frm.doc.party_account) {
frm.call({
method: "frappe.client.get_value",
args: {
doctype: "Account",
fieldname: "account_currency",
filters: { name: frm.doc.party_account },
},
callback: function(r, rt) {
if(r.message) {
frm.set_value("party_account_currency", r.message.account_currency);
erpnext.payment_tool.check_mandatory_to_set_button(frm);
}
}
});
}
})
frappe.ui.form.on("Payment Tool", "company", function(frm) { frappe.ui.form.on("Payment Tool", "company", function(frm) {
erpnext.payment_tool.check_mandatory_to_set_button(frm); erpnext.payment_tool.check_mandatory_to_set_button(frm);
}); });
@ -63,10 +88,6 @@ frappe.ui.form.on("Payment Tool", "received_or_paid", function(frm) {
erpnext.payment_tool.check_mandatory_to_set_button(frm); erpnext.payment_tool.check_mandatory_to_set_button(frm);
}); });
frappe.ui.form.on("Payment Tool", "party", function(frm) {
erpnext.payment_tool.check_mandatory_to_set_button(frm);
});
// Fetch bank/cash account based on payment mode // Fetch bank/cash account based on payment mode
frappe.ui.form.on("Payment Tool", "payment_mode", function(frm) { frappe.ui.form.on("Payment Tool", "payment_mode", function(frm) {
return frappe.call({ return frappe.call({
@ -120,6 +141,7 @@ frappe.ui.form.on("Payment Tool", "get_outstanding_vouchers", function(frm) {
c.against_voucher_no = d.voucher_no; c.against_voucher_no = d.voucher_no;
c.total_amount = d.invoice_amount; c.total_amount = d.invoice_amount;
c.outstanding_amount = d.outstanding_amount; c.outstanding_amount = d.outstanding_amount;
c.payment_amount = d.outstanding_amount;
}); });
} }
refresh_field("vouchers"); refresh_field("vouchers");
@ -130,41 +152,63 @@ frappe.ui.form.on("Payment Tool", "get_outstanding_vouchers", function(frm) {
}); });
// validate against_voucher_type // validate against_voucher_type
frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm) { frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm, cdt, cdn) {
erpnext.payment_tool.validate_against_voucher(frm); var row = frappe.model.get_doc(cdt, cdn);
erpnext.payment_tool.validate_against_voucher(frm, row);
}); });
erpnext.payment_tool.validate_against_voucher = function(frm) { erpnext.payment_tool.validate_against_voucher = function(frm, row) {
$.each(frm.doc.vouchers || [], function(i, row) { var _validate = function(i, row) {
if (!row.against_voucher_type) {
return;
}
if(frm.doc.party_type=="Customer" if(frm.doc.party_type=="Customer"
&& !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.against_voucher_type)) { && !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.against_voucher_type)) {
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", ""); frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
frappe.throw(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Entry")) frappe.msgprint(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Entry"));
return false;
} }
if(frm.doc.party_type=="Supplier" if(frm.doc.party_type=="Supplier"
&& !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.against_voucher_type)) { && !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.against_voucher_type)) {
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", ""); frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
frappe.throw(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Entry")) frappe.msgprint(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Entry"));
return false;
}
}
if (row) {
_validate(0, row);
} else {
$.each(frm.doc.vouchers || [], _validate);
} }
});
} }
// validate against_voucher_type // validate against_voucher_type
frappe.ui.form.on("Payment Tool Detail", "against_voucher_no", function(frm, cdt, cdn) { frappe.ui.form.on("Payment Tool Detail", "against_voucher_no", function(frm, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if (!row.against_voucher_no) {
return;
}
frappe.call({ frappe.call({
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_against_voucher_amount', method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_against_voucher_amount',
args: { args: {
"against_voucher_type": row.against_voucher_type, "against_voucher_type": row.against_voucher_type,
"against_voucher_no": row.against_voucher_no "against_voucher_no": row.against_voucher_no,
"party_account": frm.doc.party_account,
"company": frm.doc.company
}, },
callback: function(r) { callback: function(r) {
if(!r.exc) { if(!r.exc) {
$.each(r.message, function(k, v) { $.each(r.message, function(k, v) {
frappe.model.set_value(cdt, cdn, k, v); frappe.model.set_value(cdt, cdn, k, v);
}); });
frappe.model.set_value(cdt, cdn, "payment_amount", r.message.outstanding_amount);
} }
} }
}); });
@ -187,7 +231,7 @@ erpnext.payment_tool.set_total_payment_amount = function(frm) {
} else { } else {
if(row.payment_amount < 0) if(row.payment_amount < 0)
msgprint(__("Row {0}: Payment amount can not be negative", [row.idx])); msgprint(__("Row {0}: Payment amount can not be negative", [row.idx]));
else if(row.payment_amount >= row.outstanding_amount) else if(row.payment_amount > row.outstanding_amount)
msgprint(__("Row {0}: Payment Amount cannot be greater than Outstanding Amount", [__(row.idx)])); msgprint(__("Row {0}: Payment Amount cannot be greater than Outstanding Amount", [__(row.idx)]));
frappe.model.set_value(row.doctype, row.name, "payment_amount", 0.0); frappe.model.set_value(row.doctype, row.name, "payment_amount", 0.0);

View File

@ -106,7 +106,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Column Break 1", "label": "",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
@ -162,6 +162,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party_account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -306,6 +329,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Total Payment Amount", "label": "Total Payment Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@ -450,7 +474,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"modified": "2015-06-05 11:17:33.843334", "modified": "2015-08-31 18:58:21.813054",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Tool", "name": "Payment Tool",

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _, scrub
from frappe.utils import flt from frappe.utils import flt
from frappe.model.document import Document from frappe.model.document import Document
import json import json
@ -33,15 +33,18 @@ class PaymentTool(Document):
d1.party_type = self.party_type d1.party_type = self.party_type
d1.party = self.party d1.party = self.party
d1.balance = get_balance_on(self.party_account) d1.balance = get_balance_on(self.party_account)
d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount)) d1.set("debit_in_account_currency" if self.received_or_paid=="Paid" \
else "credit_in_account_currency", flt(v.payment_amount))
d1.set("reference_type", v.against_voucher_type) d1.set("reference_type", v.against_voucher_type)
d1.set("reference_name", v.against_voucher_no) d1.set("reference_name", v.against_voucher_no)
d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No') d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No')
total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit) total_payment_amount = flt(total_payment_amount) + \
flt(d1.debit_in_account_currency) - flt(d1.credit_in_account_currency)
d2 = jv.append("accounts") d2 = jv.append("accounts")
d2.account = self.payment_account d2.account = self.payment_account
d2.set('debit' if total_payment_amount < 0 else 'credit', abs(total_payment_amount)) d2.set('debit_in_account_currency' if total_payment_amount < 0 \
else 'credit_in_account_currency', abs(total_payment_amount))
if self.payment_account: if self.payment_account:
d2.balance = get_balance_on(self.payment_account) d2.balance = get_balance_on(self.payment_account)
@ -56,39 +59,49 @@ def get_outstanding_vouchers(args):
args = json.loads(args) args = json.loads(args)
party_account_currency = frappe.db.get_value("Account", args.get("party_account"), "account_currency")
company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
if args.get("party_type") == "Customer" and args.get("received_or_paid") == "Received": if args.get("party_type") == "Customer" and args.get("received_or_paid") == "Received":
amount_query = "ifnull(debit, 0) - ifnull(credit, 0)" amount_query = "ifnull(debit_in_account_currency, 0) - ifnull(credit_in_account_currency, 0)"
elif args.get("party_type") == "Supplier" and args.get("received_or_paid") == "Paid": elif args.get("party_type") == "Supplier" and args.get("received_or_paid") == "Paid":
amount_query = "ifnull(credit, 0) - ifnull(debit, 0)" amount_query = "ifnull(credit_in_account_currency, 0) - ifnull(debit_in_account_currency, 0)"
else: else:
frappe.throw(_("Please enter the Against Vouchers manually")) frappe.throw(_("Please enter the Against Vouchers manually"))
# Get all outstanding sales /purchase invoices # Get all outstanding sales /purchase invoices
outstanding_invoices = get_outstanding_invoices(amount_query, args.get("party_account"), outstanding_invoices = get_outstanding_invoices(amount_query, args.get("party_account"),
args.get("party_type"), args.get("party")) args.get("party_type"), args.get("party"), with_journal_entry=False)
# Get all SO / PO which are not fully billed or aginst which full advance not paid # Get all SO / PO which are not fully billed or aginst which full advance not paid
orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party")) orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"),
party_account_currency, company_currency)
return outstanding_invoices + orders_to_be_billed return outstanding_invoices + orders_to_be_billed
def get_orders_to_be_billed(party_type, party): def get_orders_to_be_billed(party_type, party, party_account_currency, company_currency):
voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order' voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order'
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
orders = frappe.db.sql(""" orders = frappe.db.sql("""
select select
name as voucher_no, name as voucher_no,
ifnull(base_grand_total, 0) as invoice_amount, ifnull({ref_field}, 0) as invoice_amount,
(ifnull(base_grand_total, 0) - ifnull(advance_paid, 0)) as outstanding_amount, (ifnull({ref_field}, 0) - ifnull(advance_paid, 0)) as outstanding_amount,
transaction_date as posting_date transaction_date as posting_date
from from
`tab%s` `tab{voucher_type}`
where where
%s = %s {party_type} = %s
and docstatus = 1 and docstatus = 1
and ifnull(status, "") != "Stopped" and ifnull(status, "") != "Stopped"
and ifnull(base_grand_total, 0) > ifnull(advance_paid, 0) and ifnull({ref_field}, 0) > ifnull(advance_paid, 0)
and abs(100 - ifnull(per_billed, 0)) > 0.01 and abs(100 - ifnull(per_billed, 0)) > 0.01
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'), """.format(**{
party, as_dict = True) "ref_field": ref_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type)
}), party, as_dict = True)
order_list = [] order_list = []
for d in orders: for d in orders:
@ -98,13 +111,19 @@ def get_orders_to_be_billed(party_type, party):
return order_list return order_list
@frappe.whitelist() @frappe.whitelist()
def get_against_voucher_amount(against_voucher_type, against_voucher_no): def get_against_voucher_amount(against_voucher_type, against_voucher_no, party_account, company):
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
company_currency = frappe.db.get_value("Company", company, "default_currency")
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
if against_voucher_type in ["Sales Order", "Purchase Order"]: if against_voucher_type in ["Sales Order", "Purchase Order"]:
select_cond = "base_grand_total as total_amount, ifnull(base_grand_total, 0) - ifnull(advance_paid, 0) as outstanding_amount" select_cond = "{0} as total_amount, ifnull({0}, 0) - ifnull(advance_paid, 0) as outstanding_amount"\
.format(ref_field)
elif against_voucher_type in ["Sales Invoice", "Purchase Invoice"]: elif against_voucher_type in ["Sales Invoice", "Purchase Invoice"]:
select_cond = "base_grand_total as total_amount, outstanding_amount" select_cond = "{0} as total_amount, outstanding_amount".format(ref_field)
elif against_voucher_type == "Journal Entry": elif against_voucher_type == "Journal Entry":
select_cond = "total_debit as total_amount" ref_field = "total_debit" if party_account_currency == company_currency else "total_debit/exchange_rate"
select_cond = "{0} as total_amount".format(ref_field)
details = frappe.db.sql("""select {0} from `tab{1}` where name = %s""" details = frappe.db.sql("""select {0} from `tab{1}` where name = %s"""
.format(select_cond, against_voucher_type), against_voucher_no, as_dict=1) .format(select_cond, against_voucher_type), against_voucher_no, as_dict=1)

View File

@ -39,7 +39,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3", "party": "_Test Customer 3",
"reference_type": "Sales Order", "reference_type": "Sales Order",
"reference_name": so2.name, "reference_name": so2.name,
"credit": 1000, "credit_in_account_currency": 1000,
"is_advance": "Yes" "is_advance": "Yes"
}) })
@ -67,7 +67,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3", "party": "_Test Customer 3",
"reference_type": si2.doctype, "reference_type": si2.doctype,
"reference_name": si2.name, "reference_name": si2.name,
"credit": 561.80 "credit_in_account_currency": 561.80
}) })
pi = self.create_voucher(pi_test_records[0], { pi = self.create_voucher(pi_test_records[0], {
@ -91,7 +91,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3", "party": "_Test Customer 3",
"party_account": "_Test Receivable - _TC", "party_account": "_Test Receivable - _TC",
"payment_mode": "Cheque", "payment_mode": "Cheque",
"payment_account": "_Test Account Bank Account - _TC", "payment_account": "_Test Bank - _TC",
"reference_no": "123456", "reference_no": "123456",
"reference_date": "2013-02-14" "reference_date": "2013-02-14"
} }
@ -117,10 +117,10 @@ class TestPaymentTool(unittest.TestCase):
def create_against_jv(self, test_record, args): def create_against_jv(self, test_record, args):
jv = frappe.copy_doc(test_record) jv = frappe.copy_doc(test_record)
jv.get("accounts")[0].update(args) jv.get("accounts")[0].update(args)
if args.get("debit"): if args.get("debit_in_account_currency"):
jv.get("accounts")[1].credit = args["debit"] jv.get("accounts")[1].credit_in_account_currency = args["debit_in_account_currency"]
elif args.get("credit"): elif args.get("credit_in_account_currency"):
jv.get("accounts")[1].debit = args["credit"] jv.get("accounts")[1].debit_in_account_currency = args["credit_in_account_currency"]
jv.insert() jv.insert()
jv.submit() jv.submit()
@ -141,7 +141,8 @@ class TestPaymentTool(unittest.TestCase):
outstanding_entries = get_outstanding_vouchers(json.dumps(args)) outstanding_entries = get_outstanding_vouchers(json.dumps(args))
for d in outstanding_entries: for d in outstanding_entries:
self.assertEquals(flt(d.get("outstanding_amount"), 2), expected_outstanding.get(d.get("voucher_type"))[1]) self.assertEquals(flt(d.get("outstanding_amount"), 2),
expected_outstanding.get(d.get("voucher_type"))[1])
self.check_jv_entries(doc, outstanding_entries, expected_outstanding) self.check_jv_entries(doc, outstanding_entries, expected_outstanding)
@ -156,11 +157,10 @@ class TestPaymentTool(unittest.TestCase):
paytool.total_payment_amount = 300 paytool.total_payment_amount = 300
new_jv = paytool.make_journal_entry() new_jv = paytool.make_journal_entry()
for jv_entry in new_jv.get("accounts"): for jv_entry in new_jv.get("accounts"):
if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"): if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"):
self.assertEquals(100.00, self.assertEquals(100.00, jv_entry.get("debit_in_account_currency"
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit")) if paytool.party_type=="Supplier" else "credit_in_account_currency"))
self.assertEquals(jv_entry.reference_name, self.assertEquals(jv_entry.reference_name,
expected_outstanding[jv_entry.reference_type][0]) expected_outstanding[jv_entry.reference_type][0])
@ -170,4 +170,6 @@ class TestPaymentTool(unittest.TestCase):
def clear_table_entries(self): def clear_table_entries(self):
frappe.db.sql("""delete from `tabGL Entry` where party in ("_Test Customer 3", "_Test Supplier 1")""") frappe.db.sql("""delete from `tabGL Entry` where party in ("_Test Customer 3", "_Test Supplier 1")""")
frappe.db.sql("""delete from `tabSales Order` where customer = "_Test Customer 3" """) frappe.db.sql("""delete from `tabSales Order` where customer = "_Test Customer 3" """)
frappe.db.sql("""delete from `tabSales Invoice` where customer = "_Test Customer 3" """)
frappe.db.sql("""delete from `tabPurchase Order` where supplier = "_Test Supplier 1" """) frappe.db.sql("""delete from `tabPurchase Order` where supplier = "_Test Supplier 1" """)
frappe.db.sql("""delete from `tabPurchase Invoice` where supplier = "_Test Supplier 1" """)

View File

@ -87,6 +87,7 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Total Amount", "label": "Total Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@ -108,6 +109,7 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Outstanding Amount", "label": "Outstanding Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@ -129,6 +131,7 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Payment Amount", "label": "Payment Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
@ -146,7 +149,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-09-11 08:55:34.384017", "modified": "2015-08-31 18:58:35.537060",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Tool Detail", "name": "Payment Tool Detail",

View File

@ -10,11 +10,11 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestPeriodClosingVoucher(unittest.TestCase): class TestPeriodClosingVoucher(unittest.TestCase):
def test_closing_entry(self): def test_closing_entry(self):
make_journal_entry("_Test Account Bank Account - _TC", "Sales - _TC", 400, make_journal_entry("_Test Bank - _TC", "Sales - _TC", 400,
"_Test Cost Center - _TC", submit=True) "_Test Cost Center - _TC", submit=True)
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 600, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 600, "_Test Cost Center - _TC", submit=True)
profit_or_loss = frappe.db.sql("""select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance profit_or_loss = frappe.db.sql("""select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance
from `tabGL Entry` t1, `tabAccount` t2 from `tabGL Entry` t1, `tabAccount` t2

View File

@ -41,7 +41,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -851,7 +851,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-13 14:58:29.194326", "modified": "2015-09-11 12:19:52.242771",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Pricing Rule", "name": "Pricing Rule",

View File

@ -76,7 +76,28 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
}) })
}, },
credit_to: function() {
var me = this;
if(this.frm.doc.credit_to) {
me.frm.call({
method: "frappe.client.get_value",
args: {
doctype: "Account",
fieldname: "account_currency",
filters: { name: me.frm.doc.credit_to },
},
callback: function(r, rt) {
if(r.message) {
me.frm.set_value("party_account_currency", r.message.account_currency);
me.set_dynamic_labels();
}
}
});
}
},
write_off_amount: function() { write_off_amount: function() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.calculate_outstanding_amount(); this.calculate_outstanding_amount();
this.frm.refresh_fields(); this.frm.refresh_fields();
}, },

View File

@ -20,7 +20,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -1266,30 +1266,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "total_amount_to_pay",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Total Amount To Pay",
"no_copy": 1,
"oldfieldname": "total_amount_to_pay",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1304,7 +1280,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "total_advance", "oldfieldname": "total_advance",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@ -1328,7 +1304,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "outstanding_amount", "oldfieldname": "outstanding_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@ -1338,6 +1314,30 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "write_off_amount",
"depends_on": "grand_total",
"fieldname": "write_off",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1350,7 +1350,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Write Off Amount", "label": "Write Off Amount",
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 0, "read_only": 0,
@ -1360,6 +1360,50 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "base_write_off_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off Amount (Company Currency)",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_61",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1406,29 +1450,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against_expense_account",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Against Expense Account",
"no_copy": 1,
"oldfieldname": "against_expense_account",
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1748,6 +1769,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party_account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1797,6 +1841,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against_expense_account",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Against Expense Account",
"no_copy": 1,
"oldfieldname": "against_expense_account",
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -2175,7 +2242,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:21:39.803805",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@ -91,7 +91,8 @@ class PurchaseInvoice(BuyingController):
throw(_("Conversion rate cannot be 0 or 1")) throw(_("Conversion rate cannot be 0 or 1"))
def validate_credit_to_acc(self): def validate_credit_to_acc(self):
account = frappe.db.get_value("Account", self.credit_to, ["account_type", "report_type"], as_dict=True) account = frappe.db.get_value("Account", self.credit_to,
["account_type", "report_type", "account_currency"], as_dict=True)
if account.report_type != "Balance Sheet": if account.report_type != "Balance Sheet":
frappe.throw(_("Credit To account must be a Balance Sheet account")) frappe.throw(_("Credit To account must be a Balance Sheet account"))
@ -99,6 +100,8 @@ class PurchaseInvoice(BuyingController):
if self.supplier and account.account_type != "Payable": if self.supplier and account.account_type != "Payable":
frappe.throw(_("Credit To account must be a Payable account")) frappe.throw(_("Credit To account must be a Payable account"))
self.party_account_currency = account.account_currency
def check_for_stopped_status(self): def check_for_stopped_status(self):
check_list = [] check_list = []
for d in self.get('items'): for d in self.get('items'):
@ -213,7 +216,7 @@ class PurchaseInvoice(BuyingController):
'party_type': 'Supplier', 'party_type': 'Supplier',
'party': self.supplier, 'party': self.supplier,
'is_advance' : 'Yes', 'is_advance' : 'Yes',
'dr_or_cr' : 'debit', 'dr_or_cr' : 'debit_in_account_currency',
'unadjusted_amt' : flt(d.advance_amount), 'unadjusted_amt' : flt(d.advance_amount),
'allocated_amt' : flt(d.allocated_amount) 'allocated_amt' : flt(d.allocated_amount)
} }
@ -257,26 +260,32 @@ class PurchaseInvoice(BuyingController):
"party_type": "Supplier", "party_type": "Supplier",
"party": self.supplier, "party": self.supplier,
"against": self.against_expense_account, "against": self.against_expense_account,
"credit": self.total_amount_to_pay, "credit": self.base_grand_total,
"remarks": self.remarks, "credit_in_account_currency": self.base_grand_total \
if self.party_account_currency==self.company_currency else self.grand_total,
"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,
}) }, self.party_account_currency)
) )
# tax table gl entries # tax table gl entries
valuation_tax = {} valuation_tax = {}
for tax in self.get("taxes"): for tax in self.get("taxes"):
if tax.category in ("Total", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount): if tax.category in ("Total", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
account_currency = frappe.db.get_value("Account", tax.account_head, "account_currency")
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": tax.account_head, "account": tax.account_head,
"against": self.supplier, "against": self.supplier,
"debit": tax.add_deduct_tax == "Add" and tax.base_tax_amount_after_discount_amount or 0, dr_or_cr: tax.base_tax_amount_after_discount_amount,
"credit": tax.add_deduct_tax == "Deduct" and tax.base_tax_amount_after_discount_amount or 0, dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
"remarks": self.remarks, if account_currency==self.company_currency \
else tax.tax_amount_after_discount_amount,
"cost_center": tax.cost_center "cost_center": tax.cost_center
}) }, account_currency)
) )
# accumulate valuation tax # accumulate valuation tax
@ -292,14 +301,16 @@ class PurchaseInvoice(BuyingController):
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):
account_currency = frappe.db.get_value("Account", item.expense_account, "account_currency")
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.expense_account, "account": item.expense_account,
"against": self.supplier, "against": self.supplier,
"debit": item.base_net_amount, "debit": item.base_net_amount,
"remarks": self.remarks, "debit_in_account_currency": item.base_net_amount \
if account_currency==self.company_currency else item.net_amount,
"cost_center": item.cost_center "cost_center": item.cost_center
}) }, account_currency)
) )
if auto_accounting_for_stock and self.is_opening == "No" and \ if auto_accounting_for_stock and self.is_opening == "No" and \
@ -352,12 +363,28 @@ class PurchaseInvoice(BuyingController):
# writeoff account includes petty difference in the invoice amount # writeoff account includes petty difference in the invoice amount
# and the amount that is paid # and the amount that is paid
if self.write_off_account and flt(self.write_off_amount): if self.write_off_account and flt(self.write_off_amount):
write_off_account_currency = frappe.db.get_value("Account", self.write_off_account, "account_currency")
gl_entries.append(
self.get_gl_dict({
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"against": self.write_off_account,
"debit": self.base_write_off_amount,
"debit_in_account_currency": self.base_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) else self.name,
"against_voucher_type": self.doctype,
}, self.party_account_currency)
)
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.write_off_account, "account": self.write_off_account,
"against": self.supplier, "against": self.supplier,
"credit": flt(self.write_off_amount), "credit": flt(self.base_write_off_amount),
"remarks": self.remarks, "credit_in_account_currency": self.base_write_off_amount \
if write_off_account_currency==self.company_currency else self.write_off_amount,
"cost_center": self.write_off_cost_center "cost_center": self.write_off_cost_center
}) })
) )

View File

@ -10,6 +10,7 @@ from frappe.utils import cint
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 test_records as pr_test_records
from erpnext.controllers.accounts_controller import InvalidCurrency
test_dependencies = ["Item", "Cost Center"] test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"] test_ignore = ["Serial No"]
@ -218,7 +219,8 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.load_from_db() pi.load_from_db()
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_type='Purchase Invoice' and reference_name=%s and debit=300""", pi.name)) where reference_type='Purchase Invoice'
and reference_name=%s and debit_in_account_currency=300""", pi.name))
self.assertEqual(pi.outstanding_amount, 1212.30) self.assertEqual(pi.outstanding_amount, 1212.30)
@ -277,6 +279,55 @@ class TestPurchaseInvoice(unittest.TestCase):
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_multi_currency_gle(self):
set_perpetual_inventory(0)
pi = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
currency="USD", conversion_rate=50)
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""", pi.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = {
"_Test Payable USD - _TC": {
"account_currency": "USD",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 12500,
"credit_in_account_currency": 250
},
"_Test Account Cost for Goods Sold - _TC": {
"account_currency": "INR",
"debit": 12500,
"debit_in_account_currency": 12500,
"credit": 0,
"credit_in_account_currency": 0
}
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[gle.account][field], gle[field])
# Check for valid currency
pi1 = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
do_not_save=True)
self.assertRaises(InvalidCurrency, pi1.save)
# cancel
pi.cancel()
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", pi.name)
self.assertFalse(gle)
def make_purchase_invoice(**args): def make_purchase_invoice(**args):
pi = frappe.new_doc("Purchase Invoice") pi = frappe.new_doc("Purchase Invoice")
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -141,6 +141,9 @@
"supplier": "_Test Supplier", "supplier": "_Test Supplier",
"supplier_name": "_Test Supplier" "supplier_name": "_Test Supplier"
}, },
{ {
"bill_no": "NA", "bill_no": "NA",
"buying_price_list": "_Test Price List", "buying_price_list": "_Test Price List",

View File

@ -117,7 +117,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "advance_amount", "oldfieldname": "advance_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "100px", "print_width": "100px",
@ -143,7 +143,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "allocated_amount", "oldfieldname": "allocated_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "100px", "print_width": "100px",
@ -164,7 +164,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-12-25 16:29:15.176476", "modified": "2015-08-25 17:51:30.274069",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Advance", "name": "Purchase Invoice Advance",

View File

@ -8,7 +8,7 @@
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.", "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -21,7 +21,7 @@
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"oldfieldname": "title", "oldfieldname": "title",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
@ -176,7 +176,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-05-06 08:52:01.499434", "modified": "2015-09-11 12:19:53.741725",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Taxes and Charges Template", "name": "Purchase Taxes and Charges Template",

View File

@ -176,6 +176,26 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}) })
}, },
debit_to: function() {
var me = this;
if(this.frm.doc.debit_to) {
me.frm.call({
method: "frappe.client.get_value",
args: {
doctype: "Account",
fieldname: "account_currency",
filters: { name: me.frm.doc.debit_to },
},
callback: function(r, rt) {
if(r.message) {
me.frm.set_value("party_account_currency", r.message.account_currency);
me.set_dynamic_labels();
}
}
});
}
},
allocated_amount: function() { allocated_amount: function() {
this.calculate_total_advance(); this.calculate_total_advance();
this.frm.refresh_fields(); this.frm.refresh_fields();
@ -183,10 +203,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
write_off_outstanding_amount_automatically: function() { write_off_outstanding_amount_automatically: function() {
if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
frappe.model.round_floats_in(this.frm.doc, ["base_grand_total", "paid_amount"]); frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
// this will make outstanding amount 0 // this will make outstanding amount 0
this.frm.set_value("write_off_amount", this.frm.set_value("write_off_amount",
flt(this.frm.doc.base_grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount"))
); );
this.frm.toggle_enable("write_off_amount", false); this.frm.toggle_enable("write_off_amount", false);
@ -199,10 +219,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}, },
write_off_amount: function() { write_off_amount: function() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.write_off_outstanding_amount_automatically(); this.write_off_outstanding_amount_automatically();
}, },
paid_amount: function() { paid_amount: function() {
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
this.write_off_outstanding_amount_automatically(); this.write_off_outstanding_amount_automatically();
}, },

View File

@ -44,7 +44,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -1145,7 +1145,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Discount", "label": "Additional Discount",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
@ -1448,7 +1448,7 @@
"no_copy": 0, "no_copy": 0,
"oldfieldname": "total_advance", "oldfieldname": "total_advance",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@ -1472,7 +1472,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "outstanding_amount", "oldfieldname": "outstanding_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@ -1663,7 +1663,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "paid_amount", "oldfieldname": "paid_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 0, "read_only": 0,
@ -1673,6 +1673,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "base_paid_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Paid Amount (Company Currency)",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -1710,7 +1733,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Write Off Amount", "label": "Write Off Amount",
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 0, "read_only": 0,
@ -1720,6 +1743,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "base_write_off_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off Amount (Company Currency)",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -2227,6 +2273,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party_account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -2882,7 +2951,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:21:06.545927",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -271,7 +271,7 @@ class SalesInvoice(SellingController):
'party_type': 'Customer', 'party_type': 'Customer',
'party': self.customer, 'party': self.customer,
'is_advance' : 'Yes', 'is_advance' : 'Yes',
'dr_or_cr' : 'credit', 'dr_or_cr' : 'credit_in_account_currency',
'unadjusted_amt' : flt(d.advance_amount), 'unadjusted_amt' : flt(d.advance_amount),
'allocated_amt' : flt(d.allocated_amount) 'allocated_amt' : flt(d.allocated_amount)
} }
@ -282,7 +282,8 @@ class SalesInvoice(SellingController):
reconcile_against_document(lst) reconcile_against_document(lst)
def validate_debit_to_acc(self): def validate_debit_to_acc(self):
account = frappe.db.get_value("Account", self.debit_to, ["account_type", "report_type"], as_dict=True) account = frappe.db.get_value("Account", self.debit_to,
["account_type", "report_type", "account_currency"], as_dict=True)
if account.report_type != "Balance Sheet": if account.report_type != "Balance Sheet":
frappe.throw(_("Debit To account must be a Balance Sheet account")) frappe.throw(_("Debit To account must be a Balance Sheet account"))
@ -290,6 +291,8 @@ class SalesInvoice(SellingController):
if self.customer and account.account_type != "Receivable": if self.customer and account.account_type != "Receivable":
frappe.throw(_("Debit To account must be a Receivable account")) frappe.throw(_("Debit To account must be a Receivable account"))
self.party_account_currency = account.account_currency
def validate_fixed_asset_account(self): def validate_fixed_asset_account(self):
"""Validate Fixed Asset and whether Income Account Entered Exists""" """Validate Fixed Asset and whether Income Account Entered Exists"""
for d in self.get('items'): for d in self.get('items'):
@ -435,7 +438,7 @@ class SalesInvoice(SellingController):
if flt(self.paid_amount) == 0: if flt(self.paid_amount) == 0:
if self.cash_bank_account: if self.cash_bank_account:
frappe.db.set(self, 'paid_amount', frappe.db.set(self, 'paid_amount',
(flt(self.base_grand_total) - flt(self.write_off_amount))) flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount")))
else: else:
# show message that the amount is not paid # show message that the amount is not paid
frappe.db.set(self,'paid_amount',0) frappe.db.set(self,'paid_amount',0)
@ -443,6 +446,9 @@ class SalesInvoice(SellingController):
else: else:
frappe.db.set(self,'paid_amount',0) frappe.db.set(self,'paid_amount',0)
frappe.db.set(self, 'base_paid_amount',
flt(self.paid_amount*self.conversion_rate, self.precision("base_paid_amount")))
def check_prev_docstatus(self): def check_prev_docstatus(self):
for d in self.get('items'): for d in self.get('items'):
if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1: if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1:
@ -498,7 +504,7 @@ class SalesInvoice(SellingController):
return gl_entries return gl_entries
def make_customer_gl_entry(self, gl_entries): def make_customer_gl_entry(self, gl_entries):
if self.base_grand_total: if self.grand_total:
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.debit_to, "account": self.debit_to,
@ -506,37 +512,42 @@ class SalesInvoice(SellingController):
"party": self.customer, "party": self.customer,
"against": self.against_income_account, "against": self.against_income_account,
"debit": self.base_grand_total, "debit": self.base_grand_total,
"remarks": self.remarks, "debit_in_account_currency": self.base_grand_total \
if self.party_account_currency==self.company_currency else self.grand_total,
"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
}) }, self.party_account_currency)
) )
def make_tax_gl_entries(self, gl_entries): def make_tax_gl_entries(self, gl_entries):
for tax in self.get("taxes"): for tax in self.get("taxes"):
if flt(tax.base_tax_amount_after_discount_amount): if flt(tax.base_tax_amount_after_discount_amount):
account_currency = frappe.db.get_value("Account", tax.account_head, "account_currency")
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": tax.account_head, "account": tax.account_head,
"against": self.customer, "against": self.customer,
"credit": flt(tax.base_tax_amount_after_discount_amount), "credit": flt(tax.base_tax_amount_after_discount_amount),
"remarks": self.remarks, "credit_in_account_currency": flt(tax.base_tax_amount_after_discount_amount) \
if account_currency==self.company_currency else flt(tax.tax_amount_after_discount_amount),
"cost_center": tax.cost_center "cost_center": tax.cost_center
}) }, account_currency)
) )
def make_item_gl_entries(self, gl_entries): def make_item_gl_entries(self, gl_entries):
# income account gl entries # income account gl entries
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):
account_currency = frappe.db.get_value("Account", item.income_account, "account_currency")
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.income_account, "account": item.income_account,
"against": self.customer, "against": self.customer,
"credit": item.base_net_amount, "credit": item.base_net_amount,
"remarks": self.remarks, "credit_in_account_currency": item.base_net_amount \
if account_currency==self.company_currency else item.net_amount,
"cost_center": item.cost_center "cost_center": item.cost_center
}) }, account_currency)
) )
# expense account gl entries # expense account gl entries
@ -546,6 +557,7 @@ class SalesInvoice(SellingController):
def make_pos_gl_entries(self, gl_entries): def make_pos_gl_entries(self, gl_entries):
if cint(self.is_pos) and self.cash_bank_account and self.paid_amount: if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
bank_account_currency = frappe.db.get_value("Account", self.cash_bank_account, "account_currency")
# POS, make payment entries # POS, make payment entries
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
@ -553,44 +565,50 @@ class SalesInvoice(SellingController):
"party_type": "Customer", "party_type": "Customer",
"party": self.customer, "party": self.customer,
"against": self.cash_bank_account, "against": self.cash_bank_account,
"credit": self.paid_amount, "credit": self.base_paid_amount,
"remarks": self.remarks, "credit_in_account_currency": self.base_paid_amount \
if self.party_account_currency==self.company_currency else self.paid_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,
}) }, self.party_account_currency)
) )
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.cash_bank_account, "account": self.cash_bank_account,
"against": self.customer, "against": self.customer,
"debit": self.paid_amount, "debit": self.base_paid_amount,
"remarks": self.remarks, "debit_in_account_currency": self.base_paid_amount \
}) if bank_account_currency==self.company_currency else self.paid_amount
}, bank_account_currency)
) )
def make_write_off_gl_entry(self, gl_entries): def make_write_off_gl_entry(self, gl_entries):
# write off entries, applicable if only pos # write off entries, applicable if only pos
if self.write_off_account and self.write_off_amount: if self.write_off_account and self.write_off_amount:
write_off_account_currency = frappe.db.get_value("Account", self.write_off_account, "account_currency")
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.debit_to, "account": self.debit_to,
"party_type": "Customer", "party_type": "Customer",
"party": self.customer, "party": self.customer,
"against": self.write_off_account, "against": self.write_off_account,
"credit": self.write_off_amount, "credit": self.base_write_off_amount,
"remarks": self.remarks, "credit_in_account_currency": self.base_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) 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
}) }, self.party_account_currency)
) )
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.write_off_account, "account": self.write_off_account,
"against": self.customer, "against": self.customer,
"debit": self.write_off_amount, "debit": self.base_write_off_amount,
"remarks": self.remarks, "debit_in_account_currency": self.base_write_off_amount \
if write_off_account_currency==self.company_currency else self.write_off_amount,
"cost_center": self.write_off_cost_center "cost_center": self.write_off_cost_center
}) }, write_off_account_currency)
) )
def get_list_context(context=None): def get_list_context(context=None):

View File

@ -7,6 +7,8 @@ import unittest, copy
from frappe.utils import nowdate, add_days, flt from frappe.utils import nowdate, add_days, flt
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.controllers.accounts_controller import InvalidCurrency
from erpnext.accounts.doctype.gl_entry.gl_entry import InvalidAccountCurrency
class TestSalesInvoice(unittest.TestCase): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):
@ -401,7 +403,7 @@ class TestSalesInvoice(unittest.TestCase):
jv.cancel() jv.cancel()
self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8) self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8)
def test_sales_invoice_gl_entry_without_aii(self): def test_sales_invoice_gl_entry_without_perpetual_inventory(self):
set_perpetual_inventory(0) set_perpetual_inventory(0)
si = frappe.copy_doc(test_records[1]) si = frappe.copy_doc(test_records[1])
si.insert() si.insert()
@ -433,7 +435,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
def test_pos_gl_entry_with_aii(self): def test_pos_gl_entry_with_perpetual_inventory(self):
set_perpetual_inventory() set_perpetual_inventory()
self.make_pos_profile() self.make_pos_profile()
@ -442,8 +444,7 @@ class TestSalesInvoice(unittest.TestCase):
pos = copy.deepcopy(test_records[1]) pos = copy.deepcopy(test_records[1])
pos["is_pos"] = 1 pos["is_pos"] = 1
pos["update_stock"] = 1 pos["update_stock"] = 1
# pos["posting_time"] = "12:05" pos["cash_bank_account"] = "_Test Bank - _TC"
pos["cash_bank_account"] = "_Test Account Bank Account - _TC"
pos["paid_amount"] = 600.0 pos["paid_amount"] = 600.0
si = frappe.copy_doc(pos) si = frappe.copy_doc(pos)
@ -474,7 +475,7 @@ class TestSalesInvoice(unittest.TestCase):
[stock_in_hand, 0.0, abs(sle.stock_value_difference)], [stock_in_hand, 0.0, abs(sle.stock_value_difference)],
[pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.0], [pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.0],
[si.debit_to, 0.0, 600.0], [si.debit_to, 0.0, 600.0],
["_Test Account Bank Account - _TC", 600.0, 0.0] ["_Test Bank - _TC", 600.0, 0.0]
]) ])
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)): for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
@ -494,7 +495,7 @@ class TestSalesInvoice(unittest.TestCase):
def make_pos_profile(self): def make_pos_profile(self):
pos_profile = frappe.get_doc({ pos_profile = frappe.get_doc({
"cash_bank_account": "_Test Account Bank Account - _TC", "cash_bank_account": "_Test Bank - _TC",
"company": "_Test Company", "company": "_Test Company",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"currency": "INR", "currency": "INR",
@ -513,7 +514,7 @@ class TestSalesInvoice(unittest.TestCase):
if not frappe.db.exists("POS Profile", "_Test POS Profile"): if not frappe.db.exists("POS Profile", "_Test POS Profile"):
pos_profile.insert() pos_profile.insert()
def test_si_gl_entry_with_aii_and_update_stock_with_warehouse_but_no_account(self): def test_si_gl_entry_with_perpetual_inventory_and_update_stock_with_warehouse_but_no_account(self):
set_perpetual_inventory() set_perpetual_inventory()
frappe.delete_doc("Account", "_Test Warehouse No Account - _TC") frappe.delete_doc("Account", "_Test Warehouse No Account - _TC")
@ -567,7 +568,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_sales_invoice_gl_entry_with_aii_no_item_code(self): def test_sales_invoice_gl_entry_with_perpetual_inventory_no_item_code(self):
set_perpetual_inventory() set_perpetual_inventory()
si = frappe.get_doc(test_records[1]) si = frappe.get_doc(test_records[1])
@ -593,7 +594,7 @@ class TestSalesInvoice(unittest.TestCase):
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_sales_invoice_gl_entry_with_aii_non_stock_item(self): def test_sales_invoice_gl_entry_with_perpetual_inventory_non_stock_item(self):
set_perpetual_inventory() set_perpetual_inventory()
si = frappe.get_doc(test_records[1]) si = frappe.get_doc(test_records[1])
si.get("items")[0].item_code = "_Test Non Stock Item" si.get("items")[0].item_code = "_Test Non Stock Item"
@ -660,7 +661,7 @@ class TestSalesInvoice(unittest.TestCase):
where reference_name=%s""", si.name)) where reference_name=%s""", si.name))
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_name=%s and credit=300""", si.name)) where reference_name=%s and credit_in_account_currency=300""", si.name))
self.assertEqual(si.outstanding_amount, 261.8) self.assertEqual(si.outstanding_amount, 261.8)
@ -842,6 +843,79 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(si.base_grand_total, 859.44) self.assertEquals(si.base_grand_total, 859.44)
self.assertEquals(si.grand_total, 859.44) self.assertEquals(si.grand_total, 859.44)
def test_multi_currency_gle(self):
set_perpetual_inventory(0)
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = {
"_Test Receivable USD - _TC": {
"account_currency": "USD",
"debit": 5000,
"debit_in_account_currency": 100,
"credit": 0,
"credit_in_account_currency": 0
},
"Sales - _TC": {
"account_currency": "INR",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
"credit_in_account_currency": 5000
}
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[gle.account][field], gle[field])
# cancel
si.cancel()
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle)
def test_invalid_currency(self):
# Customer currency = USD
# Transaction currency cannot be INR
si1 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
do_not_save=True)
self.assertRaises(InvalidCurrency, si1.save)
# Transaction currency cannot be EUR
si2 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="EUR", conversion_rate=80, do_not_save=True)
self.assertRaises(InvalidCurrency, si2.save)
# Transaction currency only allowed in USD
si3 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
# Party Account currency must be in USD, as there is existing GLE with USD
si4 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
currency="USD", conversion_rate=50, do_not_submit=True)
self.assertRaises(InvalidAccountCurrency, si4.submit)
# Party Account currency must be in USD, force customer currency as there is no GLE
si3.cancel()
si5 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
currency="USD", conversion_rate=50, do_not_submit=True)
self.assertRaises(InvalidAccountCurrency, si5.submit)
def create_sales_invoice(**args): def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice") si = frappe.new_doc("Sales Invoice")
@ -856,14 +930,15 @@ def create_sales_invoice(**args):
si.is_pos = args.is_pos si.is_pos = args.is_pos
si.is_return = args.is_return si.is_return = args.is_return
si.return_against = args.return_against si.return_against = args.return_against
si.currency="INR" si.currency=args.currency or "INR"
si.conversion_rate = 1 si.conversion_rate = args.conversion_rate or 1
si.append("items", { si.append("items", {
"item_code": args.item or args.item_code or "_Test Item", "item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC", "warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": args.qty or 1, "qty": args.qty or 1,
"rate": args.rate or 100, "rate": args.rate or 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC", "expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"serial_no": args.serial_no "serial_no": args.serial_no

View File

@ -117,7 +117,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "advance_amount", "oldfieldname": "advance_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "120px", "print_width": "120px",
@ -143,7 +143,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "allocated_amount", "oldfieldname": "allocated_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "120px", "print_width": "120px",
@ -164,7 +164,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-12-25 16:30:19.446500", "modified": "2015-08-21 16:22:28.866049",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Advance", "name": "Sales Invoice Advance",

View File

@ -8,7 +8,7 @@
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.", "description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -21,7 +21,7 @@
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"oldfieldname": "title", "oldfieldname": "title",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
@ -198,7 +198,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:19:46.488710",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Taxes and Charges Template", "name": "Sales Taxes and Charges Template",

View File

@ -31,10 +31,21 @@ def process_gl_map(gl_map, merge_entries=True):
if flt(entry.debit) < 0: if flt(entry.debit) < 0:
entry.credit = flt(entry.credit) - flt(entry.debit) entry.credit = flt(entry.credit) - flt(entry.debit)
entry.debit = 0.0 entry.debit = 0.0
if flt(entry.debit_in_account_currency) < 0:
entry.credit_in_account_currency = \
flt(entry.credit_in_account_currency) - flt(entry.debit_in_account_currency)
entry.debit_in_account_currency = 0.0
if flt(entry.credit) < 0: if flt(entry.credit) < 0:
entry.debit = flt(entry.debit) - flt(entry.credit) entry.debit = flt(entry.debit) - flt(entry.credit)
entry.credit = 0.0 entry.credit = 0.0
if flt(entry.credit_in_account_currency) < 0:
entry.debit_in_account_currency = \
flt(entry.debit_in_account_currency) - flt(entry.credit_in_account_currency)
entry.credit_in_account_currency = 0.0
return gl_map return gl_map
def merge_similar_entries(gl_map): def merge_similar_entries(gl_map):
@ -45,7 +56,11 @@ def merge_similar_entries(gl_map):
same_head = check_if_in_list(entry, merged_gl_map) same_head = check_if_in_list(entry, merged_gl_map)
if same_head: if same_head:
same_head.debit = flt(same_head.debit) + flt(entry.debit) same_head.debit = flt(same_head.debit) + flt(entry.debit)
same_head.debit_in_account_currency = \
flt(same_head.debit_in_account_currency) + flt(entry.debit_in_account_currency)
same_head.credit = flt(same_head.credit) + flt(entry.credit) same_head.credit = flt(same_head.credit) + flt(entry.credit)
same_head.credit_in_account_currency = \
flt(same_head.credit_in_account_currency) + flt(entry.credit_in_account_currency)
else: else:
merged_gl_map.append(entry) merged_gl_map.append(entry)

View File

@ -166,7 +166,7 @@ erpnext.AccountsChart = Class.extend({
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
if (me.ctype == 'Account' && node.data && node.data.balance!==undefined) { if (me.ctype == 'Account' && node.data && node.data.balance!==undefined) {
$('<span class="balance-area pull-right text-muted small">' $('<span class="balance-area pull-right text-muted small">'
+ format_currency(Math.abs(node.data.balance), node.data.currency) + format_currency(Math.abs(node.data.balance), node.data.account_currency)
+ " " + dr_or_cr + " " + dr_or_cr
+ '</span>').insertBefore(node.$ul); + '</span>').insertBefore(node.$ul);
} }
@ -214,7 +214,8 @@ erpnext.AccountsChart = Class.extend({
'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'), 'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'),
description: __("Optional. This setting will be used to filter in various transactions.") }, description: __("Optional. This setting will be used to filter in various transactions.") },
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')}, {fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')},
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"} {fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"},
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency"}
] ]
}) })

View File

@ -21,8 +21,7 @@ def get_children():
# root # root
if args['parent'] in ("Accounts", "Cost Centers"): if args['parent'] in ("Accounts", "Cost Centers"):
select_cond = ", root_type, report_type" if args["parent"]=="Accounts" else "" select_cond = ", root_type, report_type, account_currency" if ctype=="Account" else ""
acc = frappe.db.sql(""" select acc = frappe.db.sql(""" select
name as value, is_group as expandable %s name as value, is_group as expandable %s
from `tab%s` from `tab%s`
@ -35,19 +34,17 @@ def get_children():
sort_root_accounts(acc) sort_root_accounts(acc)
else: else:
# other # other
select_cond = ", account_currency" if ctype=="Account" else ""
acc = frappe.db.sql("""select acc = frappe.db.sql("""select
name as value, is_group as expandable name as value, is_group as expandable %s
from `tab%s` from `tab%s`
where ifnull(parent_%s,'') = %s where ifnull(parent_%s,'') = %s
and docstatus<2 and docstatus<2
order by name""" % (ctype, ctype.lower().replace(' ','_'), '%s'), order by name""" % (select_cond, ctype, ctype.lower().replace(' ','_'), '%s'),
args['parent'], as_dict=1) args['parent'], as_dict=1)
if ctype == 'Account': if ctype == 'Account':
currency = frappe.db.sql("select default_currency from `tabCompany` where name = %s", company)[0][0]
for each in acc: for each in acc:
bal = get_balance_on(each.get("value")) each["balance"] = flt(get_balance_on(each.get("value")))
each["currency"] = currency
each["balance"] = flt(bal)
return acc return acc

View File

@ -7,10 +7,13 @@ import frappe
import datetime import datetime
from frappe import _, msgprint, scrub from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, flt, get_first_day, date_diff, nowdate from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff
from erpnext.utilities.doctype.address.address import get_address_display from erpnext.utilities.doctype.address.address import get_address_display
from erpnext.utilities.doctype.contact.contact import get_contact_details from erpnext.utilities.doctype.contact.contact import get_contact_details
class InvalidCurrency(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
@frappe.whitelist() @frappe.whitelist()
def get_party_details(party=None, account=None, party_type="Customer", company=None, def get_party_details(party=None, account=None, party_type="Customer", company=None,
posting_date=None, price_list=None, currency=None, doctype=None): posting_date=None, price_list=None, currency=None, doctype=None):
@ -142,6 +145,60 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
} }
return out return out
def validate_accounting_currency(party):
company_currency = get_company_currency()
# set party account currency
if not party.party_account_currency:
if party.default_currency:
party.party_account_currency = party.default_currency
elif len(set(company_currency.values())) == 1:
party.party_account_currency = company_currency.values()[0]
party_account_currency_in_db = frappe.db.get_value(party.doctype, party.name, "party_account_currency")
if party_account_currency_in_db != party.party_account_currency:
existing_gle = frappe.db.get_value("GL Entry", {"party_type": party.doctype,
"party": party.name}, ["name", "account_currency"], as_dict=1)
if existing_gle:
if party_account_currency_in_db:
frappe.throw(_("Accounting Currency cannot be changed, as GL Entry exists for this {0}")
.format(party.doctype), InvalidCurrency)
else:
party.party_account_currency = existing_gle.account_currency
def validate_party_account(party):
company_currency = get_company_currency()
if party.party_account_currency:
companies_with_different_currency = []
for company, currency in company_currency.items():
if currency != party.party_account_currency:
companies_with_different_currency.append(company)
for d in party.get("accounts"):
if d.company in companies_with_different_currency:
companies_with_different_currency.remove(d.company)
selected_account_currency = frappe.db.get_value("Account", d.account, "account_currency")
if selected_account_currency != party.party_account_currency:
frappe.throw(_("Account {0} is invalid, account currency must be {1}")
.format(d.account, selected_account_currency), InvalidAccountCurrency)
if companies_with_different_currency:
frappe.msgprint(_("Please mention Default {0} Account for the following companies, as accounting currency is different from company's default currency: {1}")
.format(
"Receivable" if party.doctype=="Customer" else "Payable",
"\n" + "\n".join(companies_with_different_currency)
)
)
def get_company_currency():
company_currency = frappe._dict()
for d in frappe.get_all("Company", fields=["name", "default_currency"]):
company_currency.setdefault(d.name, d.default_currency)
return company_currency
@frappe.whitelist() @frappe.whitelist()
def get_party_account(company, party, party_type): def get_party_account(company, party, party_type):
"""Returns the account for the given `party`. """Returns the account for the given `party`.

View File

@ -30,19 +30,40 @@ class ReceivablePayableReport(object):
if args.get("party_type") == "Supplier": if args.get("party_type") == "Supplier":
columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"] columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"]
columns += [_("Invoiced Amount") + ":Currency:100", _("Paid Amount") + ":Currency:100", for label in ("Invoiced Amount", "Paid Amount", "Outstanding Amount"):
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", columns.append({
"0-" + str(self.filters.range1) + ":Currency:100", "label": label,
str(self.filters.range1) + "-" + str(self.filters.range2) + ":Currency:100", "fieldtype": "Currency",
str(self.filters.range2) + "-" + str(self.filters.range3) + ":Currency:100", "options": "currency",
str(self.filters.range3) + _("-Above") + ":Currency:100" "width": 120
] })
columns += [_("Age (Days)") + "::80"]
for label in ("0-{range1}".format(**self.filters),
"{range1}-{range2}".format(**self.filters),
"{range2}-{range3}".format(**self.filters),
"{range3}-{above}".format(range3=self.filters.range3, above=_("Above"))):
columns.append({
"label": label,
"fieldtype": "Currency",
"options": "currency",
"width": 120
})
if args.get("party_type") == "Customer": if args.get("party_type") == "Customer":
columns += [_("Territory") + ":Link/Territory:80"] columns += [_("Territory") + ":Link/Territory:80"]
if args.get("party_type") == "Supplier": if args.get("party_type") == "Supplier":
columns += [_("Supplier Type") + ":Link/Supplier Type:80"] columns += [_("Supplier Type") + ":Link/Supplier Type:80"]
columns += [_("Remarks") + "::200"] columns += [
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Data",
"width": 100,
},
_("Remarks") + "::200"
]
return columns return columns
@ -55,6 +76,8 @@ class ReceivablePayableReport(object):
future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type")) future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency")
data = [] data = []
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")): for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers): if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
@ -91,10 +114,16 @@ class ReceivablePayableReport(object):
# customer territory / supplier type # customer territory / supplier type
if args.get("party_type") == "Customer": if args.get("party_type") == "Customer":
row += [self.get_territory(gle.party), gle.remarks] row += [self.get_territory(gle.party)]
if args.get("party_type") == "Supplier": if args.get("party_type") == "Supplier":
row += [self.get_supplier_type(gle.party), gle.remarks] row += [self.get_supplier_type(gle.party)]
if self.filters.get(scrub(args.get("party_type"))):
row.append(gle.account_currency)
else:
row.append(company_currency)
row.append(gle.remarks)
data.append(row) data.append(row)
return data return data
@ -171,10 +200,17 @@ class ReceivablePayableReport(object):
def get_gl_entries(self, party_type): def get_gl_entries(self, party_type):
if not hasattr(self, "gl_entries"): if not hasattr(self, "gl_entries"):
conditions, values = self.prepare_conditions(party_type) conditions, values = self.prepare_conditions(party_type)
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party, debit, credit,
voucher_type, voucher_no, against_voucher_type, against_voucher from `tabGL Entry` if self.filters.get(scrub(party_type)):
where docstatus < 2 and party_type=%s {0} order by posting_date, party""" select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
.format(conditions), values, as_dict=True) else:
select_fields = "debit, credit"
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
voucher_type, voucher_no, against_voucher_type, against_voucher, account_currency, remarks, {0}
from `tabGL Entry`
where docstatus < 2 and party_type=%s {1} order by posting_date, party"""
.format(select_fields, conditions), values, as_dict=True)
return self.gl_entries return self.gl_entries

View File

@ -23,7 +23,8 @@ def execute(filters=None):
total_debit += flt(d[2]) total_debit += flt(d[2])
total_credit += flt(d[3]) total_credit += flt(d[3])
amounts_not_reflected_in_system = frappe.db.sql("""select sum(ifnull(jvd.debit, 0) - ifnull(jvd.credit, 0)) amounts_not_reflected_in_system = frappe.db.sql("""
select sum(ifnull(jvd.debit_in_account_currency, 0) - ifnull(jvd.credit_in_account_currency, 0))
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%s where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%s
and jv.posting_date > %s and jv.clearance_date <= %s and ifnull(jv.is_opening, 'No') = 'No' and jv.posting_date > %s and jv.clearance_date <= %s and ifnull(jv.is_opening, 'No') = 'No'
@ -38,7 +39,7 @@ def execute(filters=None):
data += [ data += [
get_balance_row(_("System Balance"), balance_as_per_system), get_balance_row(_("System Balance"), balance_as_per_system),
[""]*len(columns), [""]*len(columns),
["", '"' + _("Amounts not reflected in bank") + '"', total_debit, total_credit, "", "", "", ""], ["", '"' + _("Amounts not reflected in bank") + '"', total_debit, total_credit, "", "", "", "", ""],
get_balance_row(_("Amounts not reflected in system"), amounts_not_reflected_in_system), get_balance_row(_("Amounts not reflected in system"), amounts_not_reflected_in_system),
[""]*len(columns), [""]*len(columns),
get_balance_row(_("Expected balance as per bank"), bank_bal) get_balance_row(_("Expected balance as per bank"), bank_bal)
@ -49,13 +50,14 @@ def execute(filters=None):
def get_columns(): def get_columns():
return [_("Posting Date") + ":Date:100", _("Journal Entry") + ":Link/Journal Entry:220", return [_("Posting Date") + ":Date:100", _("Journal Entry") + ":Link/Journal Entry:220",
_("Debit") + ":Currency:120", _("Credit") + ":Currency:120", _("Debit") + ":Currency:120", _("Credit") + ":Currency:120",
_("Against Account") + ":Link/Account:200", _("Reference") + "::100", _("Ref Date") + ":Date:110", _("Clearance Date") + ":Date:110" _("Against Account") + ":Link/Account:200", _("Reference") + "::100",
_("Ref Date") + ":Date:110", _("Clearance Date") + ":Date:110", _("Currency") + ":Link/Currency:70"
] ]
def get_entries(filters): def get_entries(filters):
entries = frappe.db.sql("""select entries = frappe.db.sql("""select
jv.posting_date, jv.name, jvd.debit, jvd.credit, jv.posting_date, jv.name, jvd.debit_in_account_currency, jvd.credit_in_account_currency,
jvd.against_account, jv.cheque_no, jv.cheque_date, jv.clearance_date jvd.against_account, jv.cheque_no, jv.cheque_date, jv.clearance_date, jvd.account_currency
from from
`tabJournal Entry Account` jvd, `tabJournal Entry` jv `tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1 where jvd.parent = jv.name and jv.docstatus=1
@ -68,6 +70,6 @@ def get_entries(filters):
def get_balance_row(label, amount): def get_balance_row(label, amount):
if amount > 0: if amount > 0:
return ["", '"' + label + '"', amount, 0, "", "", "", ""] return ["", '"' + label + '"', amount, 0, "", "", "", "", ""]
else: else:
return ["", '"' + label + '"', 0, abs(amount), "", "", "", ""] return ["", '"' + label + '"', 0, abs(amount), "", "", "", "", ""]

View File

@ -12,9 +12,12 @@ def execute(filters=None):
account_details.setdefault(acc.name, acc) account_details.setdefault(acc.name, acc)
validate_filters(filters, account_details) validate_filters(filters, account_details)
validate_party(filters) validate_party(filters)
columns = get_columns() filters = set_account_currency(filters)
columns = get_columns(filters)
res = get_result(filters, account_details) res = get_result(filters, account_details)
@ -44,35 +47,76 @@ def validate_party(filters):
elif not frappe.db.exists(party_type, party): elif not frappe.db.exists(party_type, party):
frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
def get_columns(): def set_account_currency(filters):
return [_("Posting Date") + ":Date:90", _("Account") + ":Link/Account:200", if not (filters.get("account") or filters.get("party")):
_("Debit") + ":Float:100", _("Credit") + ":Float:100", return filters
else:
filters["company_currency"] = frappe.db.get_value("Company", filters.company, "default_currency")
account_currency = None
if filters.get("account"):
account_currency = frappe.db.get_value("Account", filters.account, "account_currency")
elif filters.get("party"):
gle_currency = frappe.db.get_value("GL Entry", {"party_type": filters.party_type,
"party": filters.party, "company": filters.company}, "account_currency")
if gle_currency:
account_currency = gle_currency
else:
account_currency = frappe.db.get_value(filters.party_type, filters.party, "default_currency")
filters["account_currency"] = account_currency or filters.company_currency
if filters.account_currency != filters.company_currency:
filters["show_in_account_currency"] = 1
return filters
def get_columns(filters):
columns = [
_("Posting Date") + ":Date:90", _("Account") + ":Link/Account:200",
_("Debit") + ":Float:100", _("Credit") + ":Float:100"
]
if filters.get("show_in_account_currency"):
columns += [
_("Debit") + " (" + filters.account_currency + ")" + ":Float:100",
_("Credit") + " (" + filters.account_currency + ")" + ":Float:100"
]
columns += [
_("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160", _("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160",
_("Against Account") + "::120", _("Party Type") + "::80", _("Party") + "::150", _("Against Account") + "::120", _("Party Type") + "::80", _("Party") + "::150",
_("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"] _("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"
]
return columns
def get_result(filters, account_details): def get_result(filters, account_details):
gl_entries = get_gl_entries(filters) gl_entries = get_gl_entries(filters)
data = get_data_with_opening_closing(filters, account_details, gl_entries) data = get_data_with_opening_closing(filters, account_details, gl_entries)
result = get_result_as_list(data) result = get_result_as_list(data, filters)
return result return result
def get_gl_entries(filters): def get_gl_entries(filters):
select_fields = """, sum(ifnull(debit_in_account_currency, 0)) as debit_in_account_currency,
sum(ifnull(credit_in_account_currency, 0)) as credit_in_account_currency""" \
if filters.get("show_in_account_currency") else ""
group_by_condition = "group by voucher_type, voucher_no, account, cost_center" \ group_by_condition = "group by voucher_type, voucher_no, account, cost_center" \
if filters.get("group_by_voucher") else "group by name" if filters.get("group_by_voucher") else "group by name"
gl_entries = frappe.db.sql("""select posting_date, account, party_type, party, gl_entries = frappe.db.sql("""select posting_date, account, party_type, party,
sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit, sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit,
voucher_type, voucher_no, cost_center, remarks, against, is_opening voucher_type, voucher_no, cost_center, remarks, against, is_opening {select_fields}
from `tabGL Entry` from `tabGL Entry`
where company=%(company)s {conditions} where company=%(company)s {conditions}
{group_by_condition} {group_by_condition}
order by posting_date, account"""\ order by posting_date, account"""\
.format(conditions=get_conditions(filters), group_by_condition=group_by_condition), .format(select_fields=select_fields, conditions=get_conditions(filters),
filters, as_dict=1) group_by_condition=group_by_condition), filters, as_dict=1)
return gl_entries return gl_entries
@ -105,35 +149,51 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
data = [] data = []
gle_map = initialize_gle_map(gl_entries) gle_map = initialize_gle_map(gl_entries)
opening, total_debit, total_credit, gle_map = get_accountwise_gle(filters, gl_entries, gle_map) opening, total_debit, total_credit, opening_in_account_currency, total_debit_in_account_currency, \
total_credit_in_account_currency, gle_map = get_accountwise_gle(filters, gl_entries, gle_map)
# Opening for filtered account # Opening for filtered account
if filters.get("account") or filters.get("party"): if filters.get("account") or filters.get("party"):
data += [get_balance_row(_("Opening"), opening), {}] data += [get_balance_row(_("Opening"), opening, opening_in_account_currency), {}]
for acc, acc_dict in gle_map.items(): for acc, acc_dict in gle_map.items():
if acc_dict.entries: if acc_dict.entries:
# Opening for individual ledger, if grouped by account # Opening for individual ledger, if grouped by account
if filters.get("group_by_account"): if filters.get("group_by_account"):
data.append(get_balance_row(_("Opening"), acc_dict.opening)) data.append(get_balance_row(_("Opening"), acc_dict.opening,
acc_dict.opening_in_account_currency))
data += acc_dict.entries data += acc_dict.entries
# Totals and closing for individual ledger, if grouped by account # Totals and closing for individual ledger, if grouped by account
if filters.get("group_by_account"): if filters.get("group_by_account"):
account_closing = acc_dict.opening + acc_dict.total_debit - acc_dict.total_credit
account_closing_in_account_currency = acc_dict.opening_in_account_currency \
+ acc_dict.total_debit_in_account_currency - acc_dict.total_credit_in_account_currency
data += [{"account": "'" + _("Totals") + "'", "debit": acc_dict.total_debit, data += [{"account": "'" + _("Totals") + "'", "debit": acc_dict.total_debit,
"credit": acc_dict.total_credit}, "credit": acc_dict.total_credit},
get_balance_row(_("Closing (Opening + Totals)"), get_balance_row(_("Closing (Opening + Totals)"),
(acc_dict.opening + acc_dict.total_debit - acc_dict.total_credit)), {}] account_closing, account_closing_in_account_currency), {}]
# Total debit and credit between from and to date # Total debit and credit between from and to date
if total_debit or total_credit: if total_debit or total_credit:
data.append({"account": "'" + _("Totals") + "'", "debit": total_debit, "credit": total_credit}) data.append({
"account": "'" + _("Totals") + "'",
"debit": total_debit,
"credit": total_credit,
"debit_in_account_currency": total_debit_in_account_currency,
"credit_in_account_currency": total_credit_in_account_currency
})
# Closing for filtered account # Closing for filtered account
if filters.get("account") or filters.get("party"): if filters.get("account") or filters.get("party"):
closing = opening + total_debit - total_credit
closing_in_account_currency = opening_in_account_currency + \
total_debit_in_account_currency - total_credit_in_account_currency
data.append(get_balance_row(_("Closing (Opening + Totals)"), data.append(get_balance_row(_("Closing (Opening + Totals)"),
(opening + total_debit - total_credit))) closing, closing_in_account_currency))
return data return data
@ -142,23 +202,38 @@ def initialize_gle_map(gl_entries):
for gle in gl_entries: for gle in gl_entries:
gle_map.setdefault(gle.account, frappe._dict({ gle_map.setdefault(gle.account, frappe._dict({
"opening": 0, "opening": 0,
"opening_in_account_currency": 0,
"entries": [], "entries": [],
"total_debit": 0, "total_debit": 0,
"total_debit_in_account_currency": 0,
"total_credit": 0, "total_credit": 0,
"closing": 0 "total_credit_in_account_currency": 0,
"closing": 0,
"closing_in_account_currency": 0
})) }))
return gle_map return gle_map
def get_accountwise_gle(filters, gl_entries, gle_map): def get_accountwise_gle(filters, gl_entries, gle_map):
opening, total_debit, total_credit = 0, 0, 0 opening, total_debit, total_credit = 0, 0, 0
opening_in_account_currency, total_debit_in_account_currency, total_credit_in_account_currency = 0, 0, 0
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
for gle in gl_entries: for gle in gl_entries:
amount = flt(gle.debit, 3) - flt(gle.credit, 3) amount = flt(gle.debit, 3) - flt(gle.credit, 3)
amount_in_account_currency = flt(gle.debit_in_account_currency, 3) - flt(gle.credit_in_account_currency, 3)
if (filters.get("account") or filters.get("party") or filters.get("group_by_account")) \ if (filters.get("account") or filters.get("party") or filters.get("group_by_account")) \
and (gle.posting_date < from_date or cstr(gle.is_opening) == "Yes"): and (gle.posting_date < from_date or cstr(gle.is_opening) == "Yes"):
gle_map[gle.account].opening += amount gle_map[gle.account].opening += amount
if filters.get("show_in_account_currency"):
gle_map[gle.account].opening_in_account_currency += amount_in_account_currency
if filters.get("account") or filters.get("party"): if filters.get("account") or filters.get("party"):
opening += amount opening += amount
if filters.get("show_in_account_currency"):
opening_in_account_currency += amount_in_account_currency
elif gle.posting_date <= to_date: elif gle.posting_date <= to_date:
gle_map[gle.account].entries.append(gle) gle_map[gle.account].entries.append(gle)
gle_map[gle.account].total_debit += flt(gle.debit, 3) gle_map[gle.account].total_debit += flt(gle.debit, 3)
@ -167,21 +242,43 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
total_debit += flt(gle.debit, 3) total_debit += flt(gle.debit, 3)
total_credit += flt(gle.credit, 3) total_credit += flt(gle.credit, 3)
return opening, total_debit, total_credit, gle_map if filters.get("show_in_account_currency"):
gle_map[gle.account].total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
gle_map[gle.account].total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
def get_balance_row(label, balance): total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
return { total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
return opening, total_debit, total_credit, opening_in_account_currency, \
total_debit_in_account_currency, total_credit_in_account_currency, gle_map
def get_balance_row(label, balance, balance_in_account_currency=None):
balance_row = {
"account": "'" + label + "'", "account": "'" + label + "'",
"debit": balance if balance > 0 else 0, "debit": balance if balance > 0 else 0,
"credit": -1*balance if balance < 0 else 0, "credit": -1*balance if balance < 0 else 0
} }
def get_result_as_list(data): if balance_in_account_currency != None:
balance_row.update({
"debit_in_account_currency": balance_in_account_currency if balance_in_account_currency > 0 else 0,
"credit_in_account_currency": -1*balance_in_account_currency if balance_in_account_currency < 0 else 0
})
return balance_row
def get_result_as_list(data, filters):
result = [] result = []
for d in data: for d in data:
result.append([d.get("posting_date"), d.get("account"), d.get("debit"), row = [d.get("posting_date"), d.get("account"), d.get("debit"), d.get("credit")]
d.get("credit"), d.get("voucher_type"), d.get("voucher_no"),
d.get("against"), d.get("party_type"), d.get("party"), if filters.get("show_in_account_currency"):
d.get("cost_center"), d.get("remarks")]) row += [d.get("debit_in_account_currency"), d.get("credit_in_account_currency")]
row += [d.get("voucher_type"), d.get("voucher_no"), d.get("against"),
d.get("party_type"), d.get("party"), d.get("cost_center"), d.get("remarks")
]
result.append(row)
return result return result

View File

@ -50,7 +50,7 @@ def validate_fiscal_year(date, fiscal_year, label=_("Date"), doc=None):
throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year)) throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year))
@frappe.whitelist() @frappe.whitelist()
def get_balance_on(account=None, date=None, party_type=None, party=None): def get_balance_on(account=None, date=None, party_type=None, party=None, in_account_currency=True):
if not account and frappe.form_dict.get("account"): if not account and frappe.form_dict.get("account"):
account = frappe.form_dict.get("account") account = frappe.form_dict.get("account")
if not date and frappe.form_dict.get("date"): if not date and frappe.form_dict.get("date"):
@ -94,6 +94,11 @@ def get_balance_on(account=None, date=None, party_type=None, party=None):
select name from `tabAccount` ac where ac.name = gle.account select name from `tabAccount` ac where ac.name = gle.account
and ac.lft >= %s and ac.rgt <= %s and ac.lft >= %s and ac.rgt <= %s
)""" % (acc.lft, acc.rgt)) )""" % (acc.lft, acc.rgt))
# If group and currency same as company,
# always return balance based on debit and credit in company currency
if acc.account_currency == frappe.db.get_value("Company", acc.company, "default_currency"):
in_account_currency = False
else: else:
cond.append("""gle.account = "%s" """ % (account.replace('"', '\\"'), )) cond.append("""gle.account = "%s" """ % (account.replace('"', '\\"'), ))
@ -102,10 +107,14 @@ def get_balance_on(account=None, date=None, party_type=None, party=None):
(party_type.replace('"', '\\"'), party.replace('"', '\\"'))) (party_type.replace('"', '\\"'), party.replace('"', '\\"')))
if account or (party_type and party): if account or (party_type and party):
if in_account_currency:
select_field = "sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))"
else:
select_field = "sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))"
bal = frappe.db.sql(""" bal = frappe.db.sql("""
SELECT sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) SELECT {0}
FROM `tabGL Entry` gle FROM `tabGL Entry` gle
WHERE %s""" % " and ".join(cond))[0][0] WHERE {1}""".format(select_field, " and ".join(cond)))[0][0]
# if bal is None, return 0 # if bal is None, return 0
return flt(bal) return flt(bal)
@ -194,21 +203,26 @@ def update_against_doc(d, jv_obj):
jv_detail.set("reference_name", d["against_voucher"]) jv_detail.set("reference_name", d["against_voucher"])
if d['allocated_amt'] < d['unadjusted_amt']: if d['allocated_amt'] < d['unadjusted_amt']:
jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance jvd = frappe.db.sql("""
from `tabJournal Entry Account` where name = %s""", d['voucher_detail_no']) select cost_center, balance, against_account, is_advance, account_type, exchange_rate
from `tabJournal Entry Account` where name = %s
""", d['voucher_detail_no'], as_dict=True)
# new entry with balance amount # new entry with balance amount
ch = jv_obj.append("accounts") ch = jv_obj.append("accounts")
ch.account = d['account'] ch.account = d['account']
ch.account_type = jvd[0]['account_type']
ch.exchange_rate = jvd[0]['exchange_rate']
ch.party_type = d["party_type"] ch.party_type = d["party_type"]
ch.party = d["party"] ch.party = d["party"]
ch.cost_center = cstr(jvd[0][0]) ch.cost_center = cstr(jvd[0]["cost_center"])
ch.balance = flt(jvd[0][1]) ch.balance = flt(jvd[0]["balance"])
ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt'])) ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt']))
ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0) ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0)
ch.against_account = cstr(jvd[0][2]) ch.against_account = cstr(jvd[0]["against_account"])
ch.reference_type = original_reference_type ch.reference_type = original_reference_type
ch.reference_name = original_reference_name ch.reference_name = original_reference_name
ch.is_advance = cstr(jvd[0][3]) ch.is_advance = cstr(jvd[0]["is_advance"])
ch.docstatus = 1 ch.docstatus = 1
# will work as update after submit # will work as update after submit
@ -273,7 +287,7 @@ def get_stock_and_account_difference(account_list=None, posting_date=None):
and name in (%s)""" % ', '.join(['%s']*len(account_list)), account_list)) and name in (%s)""" % ', '.join(['%s']*len(account_list)), account_list))
for account, warehouse in account_warehouse.items(): for account, warehouse in account_warehouse.items():
account_balance = get_balance_on(account, posting_date) account_balance = get_balance_on(account, posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date) stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005: if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account, flt(stock_value) - flt(account_balance)) difference.setdefault(account, flt(stock_value) - flt(account_balance))
@ -378,12 +392,12 @@ def get_stock_rbnb_difference(posting_date, company):
# Balance as per system # Balance as per system
stock_rbnb_account = "Stock Received But Not Billed - " + frappe.db.get_value("Company", company, "abbr") stock_rbnb_account = "Stock Received But Not Billed - " + frappe.db.get_value("Company", company, "abbr")
sys_bal = get_balance_on(stock_rbnb_account, posting_date) sys_bal = get_balance_on(stock_rbnb_account, posting_date, in_account_currency=False)
# Amount should be credited # Amount should be credited
return flt(stock_rbnb) + flt(sys_bal) return flt(stock_rbnb) + flt(sys_bal)
def get_outstanding_invoices(amount_query, account, party_type, party): def get_outstanding_invoices(amount_query, account, party_type, party, with_journal_entry=True):
all_outstanding_vouchers = [] all_outstanding_vouchers = []
outstanding_voucher_list = frappe.db.sql(""" outstanding_voucher_list = frappe.db.sql("""
select select
@ -411,6 +425,9 @@ def get_outstanding_invoices(amount_query, account, party_type, party):
payment_amount = -1*payment_amount[0][0] if payment_amount else 0 payment_amount = -1*payment_amount[0][0] if payment_amount else 0
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
if not with_journal_entry and d.voucher_type=="Journal Entry":
continue
if d.invoice_amount > payment_amount: if d.invoice_amount > payment_amount:
all_outstanding_vouchers.append({ all_outstanding_vouchers.append({

View File

@ -157,20 +157,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}, },
add_deduct_tax: function(doc, cdt, cdn) { add_deduct_tax: function(doc, cdt, cdn) {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
},
calculate_outstanding_amount: function() {
if(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.docstatus < 2) {
frappe.model.round_floats_in(this.frm.doc, ["base_grand_total", "total_advance", "write_off_amount"]);
this.frm.doc.total_amount_to_pay = flt(this.frm.doc.base_grand_total - this.frm.doc.write_off_amount,
precision("total_amount_to_pay"));
if (!this.frm.doc.is_return) {
this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance,
precision("outstanding_amount"));
}
}
} }
}); });
cur_frm.add_fetch('project_name', 'cost_center', 'cost_center'); cur_frm.add_fetch('project_name', 'cost_center', 'cost_center');
erpnext.buying.get_default_bom = function(frm) { erpnext.buying.get_default_bom = function(frm) {

View File

@ -43,7 +43,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -2032,7 +2032,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:19:55.502661",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -166,7 +166,7 @@ class PurchaseOrder(BuyingController):
self.update_ordered_qty() self.update_ordered_qty()
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status)) msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
self.notify_modified() self.notify_update()
clear_doctype_notifications(self) clear_doctype_notifications(self)
def on_submit(self): def on_submit(self):

View File

@ -39,16 +39,12 @@ cur_frm.cscript.make_dashboard = function(doc) {
}, },
callback: function(r) { callback: function(r) {
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) { if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
if(r.message["company_currency"].length == 1) {
cur_frm.dashboard.set_headline( cur_frm.dashboard.set_headline(
__("Total Billing This Year: ") + "<b>" __("Total Billing This Year: ") + "<b>"
+ format_currency(r.message.billing_this_year, r.message.company_currency[0]) + format_currency(r.message.billing_this_year, cur_frm.doc.party_account_currency)
+ '</b> / <span class="text-muted">' + __("Total Unpaid") + ": <b>" + '</b> / <span class="text-muted">' + __("Total Unpaid") + ": <b>"
+ format_currency(r.message.total_unpaid, r.message.company_currency[0]) + format_currency(r.message.total_unpaid, cur_frm.doc.party_account_currency)
+ '</b></span>'); + '</b></span>');
} else {
cur_frm.dashboard.set_headline("");
}
} }
cur_frm.dashboard.set_badge_count(r.message); cur_frm.dashboard.set_badge_count(r.message);
} }

View File

@ -178,7 +178,7 @@
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Default Currency", "label": "Billing Currency",
"no_copy": 1, "no_copy": 1,
"options": "Currency", "options": "Currency",
"permlevel": 0, "permlevel": 0,
@ -389,8 +389,31 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"depends_on": "eval:!doc.__islocal", "fieldname": "party_account_currency",
"description": "Mention if non-standard receivable account applicable", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Accounting Currency",
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"description": "Mention if non-standard receivable account",
"fieldname": "accounts", "fieldname": "accounts",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@ -511,7 +534,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-17 14:05:24.793609",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier", "name": "Supplier",

View File

@ -8,6 +8,7 @@ from frappe import msgprint, _
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from erpnext.utilities.address_and_contact import load_address_and_contact from erpnext.utilities.address_and_contact import load_address_and_contact
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_accounting_currency, validate_party_account
class Supplier(TransactionBase): class Supplier(TransactionBase):
def get_feed(self): def get_feed(self):
@ -45,6 +46,9 @@ class Supplier(TransactionBase):
if not self.naming_series: if not self.naming_series:
msgprint(_("Series is mandatory"), raise_exception=1) msgprint(_("Series is mandatory"), raise_exception=1)
validate_accounting_currency(self)
validate_party_account(self)
def get_contacts(self,nm): def get_contacts(self,nm):
if nm: if nm:
contact_details =frappe.db.convert_to_lists(frappe.db.sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where supplier = %s", nm)) contact_details =frappe.db.convert_to_lists(frappe.db.sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where supplier = %s", nm))
@ -89,9 +93,11 @@ def get_dashboard_info(supplier):
out[doctype] = frappe.db.get_value(doctype, out[doctype] = frappe.db.get_value(doctype,
{"supplier": supplier, "docstatus": ["!=", 2] }, "count(*)") {"supplier": supplier, "docstatus": ["!=", 2] }, "count(*)")
billing_this_year = frappe.db.sql("""select sum(base_grand_total) billing_this_year = frappe.db.sql("""
from `tabPurchase Invoice` select sum(ifnull(credit_in_account_currency, 0)) - sum(ifnull(debit_in_account_currency, 0))
where supplier=%s and docstatus = 1 and fiscal_year = %s""", from `tabGL Entry`
where voucher_type='Purchase Invoice' and party_type = 'Supplier'
and party=%s and fiscal_year = %s""",
(supplier, frappe.db.get_default("fiscal_year"))) (supplier, frappe.db.get_default("fiscal_year")))
total_unpaid = frappe.db.sql("""select sum(outstanding_amount) total_unpaid = frappe.db.sql("""select sum(outstanding_amount)

View File

@ -1,14 +1,22 @@
[ [
{ {
"company": "_Test Company",
"doctype": "Supplier", "doctype": "Supplier",
"supplier_name": "_Test Supplier", "supplier_name": "_Test Supplier",
"supplier_type": "_Test Supplier Type" "supplier_type": "_Test Supplier Type"
}, },
{ {
"company": "_Test Company",
"doctype": "Supplier", "doctype": "Supplier",
"supplier_name": "_Test Supplier 1", "supplier_name": "_Test Supplier 1",
"supplier_type": "_Test Supplier Type" "supplier_type": "_Test Supplier Type"
},
{
"doctype": "Supplier",
"supplier_name": "_Test Supplier USD",
"supplier_type": "_Test Supplier Type",
"party_account_currency": "USD",
"accounts": [{
"company": "_Test Company",
"account": "_Test Payable USD - _TC"
}]
} }
] ]

View File

@ -43,7 +43,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -1554,7 +1554,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:20:10.684388",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier Quotation", "name": "Supplier Quotation",

View File

@ -0,0 +1 @@
- **[Multi-currency Accounting](https://manual.erpnext.com/contents/accounts/multi-currency-accounting)**: You can now have an Account in a different currency than your Company's currency

View File

@ -14,8 +14,19 @@ from erpnext.controllers.sales_and_purchase_return import validate_return
force_item_fields = ("item_group", "barcode", "brand", "stock_uom") force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
class CustomerFrozen(frappe.ValidationError): pass class CustomerFrozen(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
class AccountsController(TransactionBase): class AccountsController(TransactionBase):
def __init__(self, arg1, arg2=None):
super(AccountsController, self).__init__(arg1, arg2)
@property
def company_currency(self):
if not hasattr(self, "__company_currency"):
self.__company_currency = get_company_currency(self.company)
return self.__company_currency
def validate(self): def validate(self):
if self.get("_action") and self._action != "update_after_submit": if self.get("_action") and self._action != "update_after_submit":
self.set_missing_values(for_validate=True) self.set_missing_values(for_validate=True)
@ -39,6 +50,7 @@ class AccountsController(TransactionBase):
self.validate_enabled_taxes_and_charges() self.validate_enabled_taxes_and_charges()
self.validate_party() self.validate_party()
self.validate_currency()
def on_submit(self): def on_submit(self):
if self.meta.get_field("is_recurring"): if self.meta.get_field("is_recurring"):
@ -95,8 +107,6 @@ class AccountsController(TransactionBase):
def set_price_list_currency(self, buying_or_selling): def set_price_list_currency(self, buying_or_selling):
if self.meta.get_field("currency"): if self.meta.get_field("currency"):
company_currency = get_company_currency(self.company)
# price list part # price list part
fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \ fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
else "buying_price_list" else "buying_price_list"
@ -104,22 +114,22 @@ class AccountsController(TransactionBase):
self.price_list_currency = frappe.db.get_value("Price List", self.price_list_currency = frappe.db.get_value("Price List",
self.get(fieldname), "currency") self.get(fieldname), "currency")
if self.price_list_currency == company_currency: if self.price_list_currency == self.company_currency:
self.plc_conversion_rate = 1.0 self.plc_conversion_rate = 1.0
elif not self.plc_conversion_rate: elif not self.plc_conversion_rate:
self.plc_conversion_rate = get_exchange_rate( self.plc_conversion_rate = get_exchange_rate(
self.price_list_currency, company_currency) self.price_list_currency, self.company_currency)
# currency # currency
if not self.currency: if not self.currency:
self.currency = self.price_list_currency self.currency = self.price_list_currency
self.conversion_rate = self.plc_conversion_rate self.conversion_rate = self.plc_conversion_rate
elif self.currency == company_currency: elif self.currency == self.company_currency:
self.conversion_rate = 1.0 self.conversion_rate = 1.0
elif not self.conversion_rate: elif not self.conversion_rate:
self.conversion_rate = get_exchange_rate(self.currency, self.conversion_rate = get_exchange_rate(self.currency,
company_currency) self.company_currency)
def set_missing_item_details(self): def set_missing_item_details(self):
"""set missing item values""" """set missing item values"""
@ -187,7 +197,7 @@ class AccountsController(TransactionBase):
if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"):
frappe.throw(_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges)) frappe.throw(_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges))
def get_gl_dict(self, args): def get_gl_dict(self, args, account_currency=None):
"""this method populates the common properties of a gl entry record""" """this method populates the common properties of a gl entry record"""
gl_dict = frappe._dict({ gl_dict = frappe._dict({
'company': self.company, 'company': self.company,
@ -198,11 +208,51 @@ class AccountsController(TransactionBase):
'fiscal_year': self.fiscal_year, 'fiscal_year': self.fiscal_year,
'debit': 0, 'debit': 0,
'credit': 0, 'credit': 0,
'debit_in_account_currency': 0,
'credit_in_account_currency': 0,
'is_opening': self.get("is_opening") or "No", 'is_opening': self.get("is_opening") or "No",
'party_type': None, 'party_type': None,
'party': None 'party': None
}) })
gl_dict.update(args) gl_dict.update(args)
if not account_currency:
account_currency = frappe.db.get_value("Account", gl_dict.account, "account_currency")
self.validate_account_currency(gl_dict.account, account_currency)
gl_dict = self.set_balance_in_account_currency(gl_dict, account_currency)
return gl_dict
def validate_account_currency(self, account, account_currency=None):
if self.doctype == "Journal Entry":
return
valid_currency = [self.company_currency]
if self.get("currency") and self.currency != self.company_currency:
valid_currency.append(self.currency)
if account_currency not in valid_currency:
frappe.throw(_("Account {0} is invalid. Account Currency must be {1}")
.format(account, _(" or ").join(valid_currency)))
def set_balance_in_account_currency(self, gl_dict, account_currency=None):
if (not self.get("conversion_rate") and self.doctype!="Journal Entry"
and account_currency!=self.company_currency):
frappe.throw(_("Account: {0} with currency: {1} can not be selected")
.format(gl_dict.account, account_currency))
gl_dict["account_currency"] = self.company_currency if account_currency==self.company_currency \
else account_currency
# set debit/credit in account currency if not provided
if flt(gl_dict.debit) and not flt(gl_dict.debit_in_account_currency):
gl_dict.debit_in_account_currency = gl_dict.debit if account_currency==self.company_currency \
else flt(gl_dict.debit / (self.get("conversion_rate")), 2)
if flt(gl_dict.credit) and not flt(gl_dict.credit_in_account_currency):
gl_dict.credit_in_account_currency = gl_dict.credit if account_currency==self.company_currency \
else flt(gl_dict.credit / (self.get("conversion_rate")), 2)
return gl_dict return gl_dict
def clear_unallocated_advances(self, childtype, parentfield): def clear_unallocated_advances(self, childtype, parentfield):
@ -321,9 +371,9 @@ class AccountsController(TransactionBase):
def set_total_advance_paid(self): def set_total_advance_paid(self):
if self.doctype == "Sales Order": if self.doctype == "Sales Order":
dr_or_cr = "credit" dr_or_cr = "credit_in_account_currency"
else: else:
dr_or_cr = "debit" dr_or_cr = "debit_in_account_currency"
advance_paid = frappe.db.sql(""" advance_paid = frappe.db.sql("""
select select
@ -331,10 +381,9 @@ class AccountsController(TransactionBase):
from from
`tabJournal Entry Account` `tabJournal Entry Account`
where where
reference_type = %s and reference_type = %s and reference_name = %s
reference_name = %s and and docstatus = 1 and is_advance = "Yes"
docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr), """.format(dr_or_cr=dr_or_cr), (self.doctype, self.name))
(self.doctype, self.name))
if advance_paid: if advance_paid:
advance_paid = flt(advance_paid[0][0], self.precision("advance_paid")) advance_paid = flt(advance_paid[0][0], self.precision("advance_paid"))
@ -357,6 +406,13 @@ class AccountsController(TransactionBase):
if frozen_accounts_modifier in frappe.get_roles(): if frozen_accounts_modifier in frappe.get_roles():
return return
party_type, party = self.get_party()
if party_type:
if frappe.db.get_value(party_type, party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen)
def get_party(self):
party_type = None party_type = None
if self.meta.get_field("customer"): if self.meta.get_field("customer"):
party_type = 'Customer' party_type = 'Customer'
@ -364,10 +420,20 @@ class AccountsController(TransactionBase):
elif self.meta.get_field("supplier"): elif self.meta.get_field("supplier"):
party_type = 'Supplier' party_type = 'Supplier'
if party_type: party = self.get(party_type.lower()) if party_type else None
party = self.get(party_type.lower())
if frappe.db.get_value(party_type, party, "is_frozen"): return party_type, party
frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen)
def validate_currency(self):
if self.get("currency"):
party_type, party = self.get_party()
if party_type and party:
party_account_currency = frappe.db.get_value(party_type, party, "party_account_currency") \
or self.company_currency
if party_account_currency != self.company_currency and self.currency != party_account_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(party_type, party, party_account_currency), InvalidCurrency)
@frappe.whitelist() @frappe.whitelist()
def get_tax_rate(account_head): def get_tax_rate(account_head):

View File

@ -61,7 +61,7 @@ def validate_item_variant_attributes(item, args):
frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3}")\ frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3}")\
.format(attribute, from_range, to_range, increment), InvalidItemAttributeValueError) .format(attribute, from_range, to_range, increment), InvalidItemAttributeValueError)
elif value not in attribute_values[attribute]: elif value not in attribute_values.get(attribute, []):
frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values").format( frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values").format(
value, attribute)) value, attribute))

View File

@ -9,6 +9,7 @@ from frappe.utils import nowdate
def get_filters_cond(doctype, filters, conditions): def get_filters_cond(doctype, filters, conditions):
if filters: if filters:
flt = filters
if isinstance(filters, dict): if isinstance(filters, dict):
filters = filters.items() filters = filters.items()
flt = [] flt = []

View File

@ -4,7 +4,7 @@ import frappe.utils
import frappe.defaults import frappe.defaults
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \ from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
get_first_day, get_last_day, comma_and get_first_day, get_last_day, comma_and, split_emails
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe import _, msgprint, throw from frappe import _, msgprint, throw
@ -180,8 +180,7 @@ def convert_to_recurring(doc, posting_date):
def validate_notification_email_id(doc): def validate_notification_email_id(doc):
if doc.notification_email_address: if doc.notification_email_address:
email_list = filter(None, [cstr(email).strip() for email in email_list = split_emails(doc.notification_email_address.replace("\n", ""))
doc.notification_email_address.replace("\n", "").split(",")])
from frappe.utils import validate_email_add from frappe.utils import validate_email_add
for email in email_list: for email in email_list:

View File

@ -222,7 +222,7 @@ class StatusUpdater(Document):
where name='%(name)s'""" % args) where name='%(name)s'""" % args)
if args.get("set_modified"): if args.get("set_modified"):
frappe.get_doc(args["target_parent_dt"], name).notify_modified() frappe.get_doc(args["target_parent_dt"], name).notify_update()
def update_billing_status_for_zero_amount_refdoc(self, ref_dt): def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
ref_fieldname = ref_dt.lower().replace(" ", "_") ref_fieldname = ref_dt.lower().replace(" ", "_")

View File

@ -48,20 +48,20 @@ class StockController(AccountsController):
self.check_expense_account(detail) self.check_expense_account(detail)
gl_list.append(self.get_gl_dict({ gl_list.append(self.get_gl_dict({
"account": warehouse_account[sle.warehouse], "account": warehouse_account[sle.warehouse]["name"],
"against": detail.expense_account, "against": detail.expense_account,
"cost_center": detail.cost_center, "cost_center": detail.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock", "remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": flt(sle.stock_value_difference, 2) "debit": flt(sle.stock_value_difference, 2),
})) }, warehouse_account[sle.warehouse]["account_currency"]))
# to target warehouse / expense account # to target warehouse / expense account
gl_list.append(self.get_gl_dict({ gl_list.append(self.get_gl_dict({
"account": detail.expense_account, "account": detail.expense_account,
"against": warehouse_account[sle.warehouse], "against": warehouse_account[sle.warehouse]["name"],
"cost_center": detail.cost_center, "cost_center": detail.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock", "remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2) "credit": flt(sle.stock_value_difference, 2),
})) }))
elif sle.warehouse not in warehouse_with_no_account: elif sle.warehouse not in warehouse_with_no_account:
warehouse_with_no_account.append(sle.warehouse) warehouse_with_no_account.append(sle.warehouse)
@ -336,6 +336,9 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
return gl_entries return gl_entries
def get_warehouse_account(): def get_warehouse_account():
warehouse_account = dict(frappe.db.sql("""select warehouse, name from tabAccount warehouse_account = frappe._dict()
where account_type = 'Warehouse' and ifnull(warehouse, '') != ''"""))
for d in frappe.db.sql("""select warehouse, name, account_currency from tabAccount
where account_type = 'Warehouse' and ifnull(warehouse, '') != ''""", as_dict=1):
warehouse_account.setdefault(d.warehouse, d)
return warehouse_account return warehouse_account

View File

@ -394,17 +394,21 @@ class calculate_taxes_and_totals(object):
# NOTE: # NOTE:
# write_off_amount is only for POS Invoice # write_off_amount is only for POS Invoice
# total_advance is only for non POS Invoice # total_advance is only for non POS Invoice
if self.doc.is_return:
return
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
total_amount_to_pay = flt(self.doc.grand_total - self.doc.total_advance - self.doc.write_off_amount,
self.doc.precision("grand_total"))
if self.doc.doctype == "Sales Invoice": if self.doc.doctype == "Sales Invoice":
if not self.doc.is_return: self.doc.round_floats_in(self.doc, ["paid_amount"])
self.doc.round_floats_in(self.doc, ["base_grand_total", "total_advance", "write_off_amount", "paid_amount"]) outstanding_amount = flt(total_amount_to_pay - self.doc.paid_amount, self.doc.precision("outstanding_amount"))
total_amount_to_pay = self.doc.base_grand_total - self.doc.write_off_amount elif self.doc.doctype == "Purchase Invoice":
self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount, outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
self.doc.precision("outstanding_amount"))
if self.doc.party_account_currency == self.doc.currency:
self.doc.outstanding_amount = outstanding_amount
else: else:
self.doc.round_floats_in(self.doc, ["total_advance", "write_off_amount"]) self.doc.outstanding_amount = flt(outstanding_amount * self.doc.conversion_rate,
self.doc.total_amount_to_pay = flt(self.doc.base_grand_total - self.doc.write_off_amount,
self.doc.precision("total_amount_to_pay"))
if not self.doc.is_return:
self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
self.doc.precision("outstanding_amount")) self.doc.precision("outstanding_amount"))

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Document",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -808,7 +808,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-27 03:26:48.324207", "modified": "2015-09-14 02:57:27.841011",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Lead", "name": "Lead",

View File

@ -20,7 +20,7 @@ class Newsletter(Document):
group by status""", (self.doctype, self.name))) or None group by status""", (self.doctype, self.name))) or None
def test_send(self, doctype="Lead"): def test_send(self, doctype="Lead"):
self.recipients = self.test_email_id.split(",") self.recipients = frappe.utils.split_emails(self.test_email_id)
self.send_bulk() self.send_bulk()
frappe.msgprint(_("Scheduled to send to {0}").format(self.test_email_id)) frappe.msgprint(_("Scheduled to send to {0}").format(self.test_email_id))

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -20,7 +20,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -62,7 +62,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-07-15 07:18:30.094155", "modified": "2015-09-11 12:20:05.237636",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Newsletter List", "name": "Newsletter List",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction", "document_type": "Document",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -84,7 +84,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-03-23 04:47:36.505462", "modified": "2015-09-14 02:55:58.113810",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Newsletter List Subscriber", "name": "Newsletter List Subscriber",

View File

@ -185,7 +185,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -889,7 +889,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:19:38.052900",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

@ -6,6 +6,7 @@ import frappe, json
from frappe.utils import cstr, cint from frappe.utils import cstr, cint
from frappe import msgprint, _ from frappe import msgprint, _
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.setup.utils import get_exchange_rate
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
@ -179,7 +180,17 @@ def get_item_details(item_code):
def make_quotation(source_name, target_doc=None): def make_quotation(source_name, target_doc=None):
def set_missing_values(source, target): def set_missing_values(source, target):
quotation = frappe.get_doc(target) quotation = frappe.get_doc(target)
quotation.currency = None # set it as default from customer
company_currency = frappe.db.get_value("Company", quotation.company, "default_currency")
party_account_currency = frappe.db.get_value("Customer", quotation.customer, "party_account_currency")
if company_currency == party_account_currency:
exchange_rate = 1
else:
exchange_rate = get_exchange_rate(party_account_currency, company_currency)
quotation.currency = party_account_currency or company_currency
quotation.conversion_rate = exchange_rate
quotation.run_method("set_missing_values") quotation.run_method("set_missing_values")
quotation.run_method("calculate_taxes_and_totals") quotation.run_method("calculate_taxes_and_totals")

View File

@ -27,7 +27,7 @@ blogs.
""" """
app_icon = "icon-th" app_icon = "icon-th"
app_color = "#e74c3c" app_color = "#e74c3c"
app_version = "6.1.1" app_version = "6.2.0"
github_link = "https://github.com/frappe/erpnext" github_link = "https://github.com/frappe/erpnext"
error_report_email = "support@erpnext.com" error_report_email = "support@erpnext.com"
@ -120,7 +120,7 @@ scheduler_events = {
] ]
} }
default_mail_footer = """<div style="padding: 15px; text-align: center;"> default_mail_footer = """<div style="text-align: center;">
<a href="https://erpnext.com?source=via_email_footer" target="_blank" style="color: #8d99a6;"> <a href="https://erpnext.com?source=via_email_footer" target="_blank" style="color: #8d99a6;">
Sent via ERPNext Sent via ERPNext
</a> </a>

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -91,7 +91,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:53.800359",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Appraisal Template", "name": "Appraisal Template",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -277,7 +277,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:53.875571",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Attendance", "name": "Attendance",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -42,7 +42,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:54.431393",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Branch", "name": "Branch",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -66,7 +66,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:55.387298",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Deduction Type", "name": "Deduction Type",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -65,7 +65,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:55.449257",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Department", "name": "Department",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -42,7 +42,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:55.508901",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Designation", "name": "Designation",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -66,7 +66,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:55.558890",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Earning Type", "name": "Earning Type",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -1893,7 +1893,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:55.671692",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee", "name": "Employee",

View File

@ -158,8 +158,13 @@ def get_retirement_date(date_of_birth=None):
import datetime import datetime
ret = {} ret = {}
if date_of_birth: if date_of_birth:
try:
dt = getdate(date_of_birth) + datetime.timedelta(21915) dt = getdate(date_of_birth) + datetime.timedelta(21915)
ret = {'date_of_retirement': dt.strftime('%Y-%m-%d')} ret = {'date_of_retirement': dt.strftime('%Y-%m-%d')}
except ValueError:
# invalid date
ret = {}
return ret return ret
@frappe.whitelist() @frappe.whitelist()

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -42,7 +42,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:56.145539",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employment Type", "name": "Employment Type",

View File

@ -467,7 +467,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -536,7 +536,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-11 12:20:16.578324",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Expense Claim", "name": "Expense Claim",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -89,7 +89,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-07-15 08:57:23.069980", "modified": "2015-09-14 02:55:56.204289",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Expense Claim Type", "name": "Expense Claim Type",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -177,7 +177,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-02-05 05:11:39.099428", "modified": "2015-09-14 02:55:56.430032",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Holiday List", "name": "Holiday List",

View File

@ -8,7 +8,7 @@
"description": "Applicant for a Job", "description": "Applicant for a Job",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction", "document_type": "Document",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -191,7 +191,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:57.100488",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Job Applicant", "name": "Job Applicant",

View File

@ -8,7 +8,7 @@
"description": "Description of a Job Opening", "description": "Description of a Job Opening",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction", "document_type": "Document",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -85,7 +85,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:57.210679",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Job Opening", "name": "Job Opening",

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Job Opening')
class TestJobOpening(unittest.TestCase):
pass

View File

@ -8,7 +8,7 @@
"description": "Apply / Approve Leaves", "description": "Apply / Approve Leaves",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction", "document_type": "Document",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -559,7 +559,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 3, "max_attachments": 3,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:57.427750",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Application", "name": "Leave Application",

View File

@ -8,7 +8,7 @@
"description": "Block Holidays on important days.", "description": "Block Holidays on important days.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -195,7 +195,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:57.796873",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Block List", "name": "Leave Block List",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -174,7 +174,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:57.897034",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Type", "name": "Leave Type",

View File

@ -8,7 +8,7 @@
"default_print_format": "Offer Letter", "default_print_format": "Offer Letter",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Transaction", "document_type": "Document",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -310,7 +310,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-07-29 05:51:39.841591", "modified": "2015-09-14 02:55:58.358852",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Offer Letter", "name": "Offer Letter",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -39,7 +39,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-03-10 18:19:23.032194", "modified": "2015-09-14 02:55:58.508655",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Offer Term", "name": "Offer Term",

View File

@ -147,9 +147,7 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) {
cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) { cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) {
return{ return{
query: "erpnext.controllers.queries.item_query", query: "erpnext.controllers.queries.item_query",
filters: { filters: [["Item", "name", "!=", doc.item]]
"name": "!" + cstr(doc.item)
}
} }
} }

View File

@ -6,7 +6,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -713,7 +713,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:55:54.208705",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM", "name": "BOM",

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -127,7 +127,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-02-22 10:24:26.834166", "modified": "2015-09-14 02:55:58.575636",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Operation", "name": "Operation",

View File

@ -109,7 +109,7 @@ class ProductionOrder(Document):
self.update_status(status) self.update_status(status)
self.update_planned_qty() self.update_planned_qty()
frappe.msgprint(_("Production Order status is {0}").format(status)) frappe.msgprint(_("Production Order status is {0}").format(status))
self.notify_modified() self.notify_update()
def update_status(self, status=None): def update_status(self, status=None):

View File

@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -341,7 +341,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-09-07 15:51:26", "modified": "2015-09-14 02:56:01.492085",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Workstation", "name": "Workstation",

View File

@ -206,3 +206,4 @@ execute:frappe.db.set_value("Stock Settings", None, "automatically_set_serial_no
execute:frappe.db.sql("""update `tabProject` set percent_complete=round(percent_complete, 2) where percent_complete is not null""") execute:frappe.db.sql("""update `tabProject` set percent_complete=round(percent_complete, 2) where percent_complete is not null""")
erpnext.patches.v6_0.fix_outstanding_amount erpnext.patches.v6_0.fix_outstanding_amount
erpnext.patches.v6_0.fix_planned_qty erpnext.patches.v6_0.fix_planned_qty
erpnext.patches.v6_0.multi_currency

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