Merge branch 'nabinhait-multi-currency' into develop

Conflicts:
	erpnext/accounts/doctype/journal_entry/journal_entry.json
This commit is contained in:
Anand Doshi
2015-09-11 16:35:17 +05:30
61 changed files with 3180 additions and 1625 deletions

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,

View File

@@ -30,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"""
@@ -89,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

@@ -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,

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,16 +76,26 @@ 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,
return { is_group: 0
filters: { };
company: me.frm.doc.company, if(!doc.multi_currency) {
is_group: 0 $.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 {
filters: {
company: me.frm.doc.company,
is_group: 0
}
};
}); });
me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) { me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
@@ -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_type==="Purchase Invoice" && !flt(d.debit)) { if(d.reference_name) {
this.get_outstanding('Purchase Invoice', d.reference_name, d); if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
} this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d);
if (d.reference_type==="Sales Invoice" && !flt(d.credit)) { }
this.get_outstanding('Sales Invoice', d.reference_name, d); if (d.reference_type==="Sales Invoice" && !flt(d.credit)) {
} this.get_outstanding('Sales Invoice', d.reference_name, doc.company, d);
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) { }
this.get_outstanding('Journal Entry', d.reference_name, d); if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
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,18 +331,63 @@ 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", {
var d = frappe.get_doc(cdt, cdn); party: function(frm, cdt, cdn) {
if(!d.account && d.party_type && d.party) { var d = frappe.get_doc(cdt, cdn);
return frm.call({ if(!d.account && d.party_type && d.party) {
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance", return frm.call({
child: d, method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance",
args: { child: d,
company: frm.doc.company, args: {
party_type: d.party_type, company: frm.doc.company,
party: d.party party_type: d.party_type,
} party: d.party
}); }
});
}
},
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);
} }
}) })
@@ -322,3 +395,41 @@ 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

@@ -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-11 12:19:50.635624", "modified": "2015-09-11 12:20: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
for d in self.get('accounts'): if diff:
if not d.credit and not d.debit and diff != 0: for d in self.get('accounts'):
if diff>0: if not d.credit_in_account_currency and not d.debit_in_account_currency and diff != 0:
d.credit = diff blank_row = d
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")
amount = flt(so.base_grand_total) - flt(so.advance_paid) 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)
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")
amount = flt(po.base_grand_total) - flt(po.advance_paid) 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)
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,32 +165,80 @@ 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
} }
]) ])
jv.insert() if save or submit:
jv.insert()
if submit: if submit:
jv.submit() jv.submit()
return jv return jv

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-09 12:55:59.270539",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",

View File

@@ -55,6 +55,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 +82,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({
@@ -158,7 +173,9 @@ frappe.ui.form.on("Payment Tool Detail", "against_voucher_no", function(frm, cdt
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": self.party_account,
"company": self.company
}, },
callback: function(r) { callback: function(r) {
if(!r.exc) { if(!r.exc) {

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,10 +59,13 @@ 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"))
@@ -68,27 +74,34 @@ def get_outstanding_vouchers(args):
args.get("party_type"), args.get("party")) args.get("party_type"), args.get("party"))
# 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

@@ -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

@@ -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,

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

@@ -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

@@ -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,

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

@@ -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

@@ -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

@@ -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
@@ -91,10 +112,11 @@ 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)]
row += [gle.account_currency, gle.remarks]
data.append(row) data.append(row)
return data return data
@@ -171,10 +193,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"):
@@ -102,10 +102,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 +198,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 +282,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,7 +387,7 @@ 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)

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

@@ -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,

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))

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

@@ -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

@@ -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("outstanding_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"))

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

@@ -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

View File

@@ -0,0 +1,106 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
# Reload doctype
for dt in ("Account", "GL Entry", "Journal Entry",
"Journal Entry Account", "Sales Invoice", "Purchase Invoice", "Customer", "Supplier"):
frappe.reload_doctype(dt)
for company in frappe.get_all("Company", fields=["name", "default_currency", "default_receivable_account"]):
# update currency in account and gl entry as per company currency
frappe.db.sql("""update `tabAccount` set account_currency = %s
where ifnull(account_currency, '') = '' and company=%s""", (company.default_currency, company.name))
# update newly introduced field's value in sales / purchase invoice
frappe.db.sql("""
update
`tabSales Invoice`
set
base_paid_amount=paid_amount,
base_write_off_amount=write_off_amount,
party_account_currency=%s
where company=%s
""", (company.default_currency, company.name))
frappe.db.sql("""
update
`tabPurchase Invoice`
set
base_write_off_amount=write_off_amount,
party_account_currency=%s
where company=%s
""", (company.default_currency, company.name))
# update exchange rate, debit/credit in account currency in Journal Entry
frappe.db.sql("""
update `tabJournal Entry Account` jea
set exchange_rate=1,
debit_in_account_currency=debit,
credit_in_account_currency=credit,
account_type=(select account_type from `tabAccount` where name=jea.account)
""")
frappe.db.sql("""
update `tabJournal Entry Account` jea, `tabJournal Entry` je
set account_currency=%s
where jea.parent = je.name and je.company=%s
""", (company.default_currency, company.name))
# update debit/credit in account currency in GL Entry
frappe.db.sql("""
update
`tabGL Entry`
set
debit_in_account_currency=debit,
credit_in_account_currency=credit,
account_currency=%s
where
company=%s
""", (company.default_currency, company.name))
# Set party account if default currency of party other than company's default currency
for dt in ("Customer", "Supplier"):
parties = frappe.get_all(dt)
for p in parties:
# Get party GL Entries
party_gle = frappe.db.get_value("GL Entry", {"party_type": dt, "party": p.name,
"company": company.name}, ["account", "account_currency"], as_dict=True)
party = frappe.get_doc(dt, p.name)
# set party account currency
if party_gle or not party.party_account_currency:
party.party_account_currency = company.default_currency
# Add default receivable /payable account if not exists
# and currency is other than company currency
if party.party_account_currency and party.party_account_currency != company.default_currency:
party_account_exists = False
for d in party.get("accounts"):
if d.company == company.name:
party_account_exists = True
break
if not party_account_exists:
party_account = None
if party_gle:
party_account = party_gle.account
else:
default_receivable_account_currency = frappe.db.get_value("Account",
company.default_receivable_account, "account_currency")
if default_receivable_account_currency != company.default_currency:
party_account = company.default_receivable_account
if party_account:
party.append("accounts", {
"company": company.name,
"account": party_account
})
party.flags.ignore_mandatory = True
party.save()

View File

@@ -491,5 +491,45 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({
this.frm.doc.total_advance = flt(total_allocated_amount, precision("total_advance")); this.frm.doc.total_advance = flt(total_allocated_amount, precision("total_advance"));
this.calculate_outstanding_amount(update_paid_amount); this.calculate_outstanding_amount(update_paid_amount);
},
calculate_outstanding_amount: function(update_paid_amount) {
// NOTE:
// paid_amount and write_off_amount is only for POS Invoice
// total_advance is only for non POS Invoice
if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return;
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]);
var total_amount_to_pay = flt((this.frm.doc.grand_total - this.frm.doc.total_advance
- this.frm.doc.write_off_amount), precision("grand_total"));
if(this.frm.doc.doctype == "Sales Invoice") {
frappe.model.round_floats_in(this.frm.doc, ["paid_amount"]);
if(this.frm.doc.is_pos) {
if(!this.frm.doc.paid_amount || update_paid_amount===undefined || update_paid_amount) {
this.frm.doc.paid_amount = flt(total_amount_to_pay);
}
} else {
this.frm.doc.paid_amount = 0
}
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
this.frm.refresh_field("paid_amount");
this.frm.refresh_field("base_paid_amount");
var outstanding_amount = flt(total_amount_to_pay - this.frm.doc.paid_amount,
precision("outstanding_amount"));
} else if(this.frm.doc.doctype == "Purchase Invoice") {
var outstanding_amount = flt(total_amount_to_pay, precision("outstanding_amount"));
}
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
this.frm.set_value("outstanding_amount", outstanding_amount);
} else {
this.frm.set_value("outstanding_amount",
flt(outstanding_amount * this.frm.doc.conversion_rate, precision("outstanding_amount")));
}
} }
}) })

View File

@@ -423,11 +423,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
setup_field_label_map(["base_total", "base_net_total", "base_total_taxes_and_charges", setup_field_label_map(["base_total", "base_net_total", "base_total_taxes_and_charges",
"base_discount_amount", "base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount", "base_grand_total", "base_rounded_total", "base_in_words",
"base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "total_amount_to_pay", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "total_amount_to_pay",
"outstanding_amount", "total_advance", "paid_amount", "write_off_amount"], company_currency); "base_paid_amount", "base_write_off_amount"
], company_currency);
setup_field_label_map(["total", "net_total", "total_taxes_and_charges", "discount_amount", setup_field_label_map(["total", "net_total", "total_taxes_and_charges", "discount_amount",
"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted", "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted",
"rounded_total", "in_words"], this.frm.doc.currency); "rounded_total", "in_words", "paid_amount", "write_off_amount"], this.frm.doc.currency);
setup_field_label_map(["outstanding_amount", "total_advance"], this.frm.doc.party_account_currency);
cur_frm.set_df_property("conversion_rate", "description", "1 " + this.frm.doc.currency cur_frm.set_df_property("conversion_rate", "description", "1 " + this.frm.doc.currency
+ " = [?] " + company_currency) + " = [?] " + company_currency)
@@ -440,7 +443,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
// toggle fields // toggle fields
this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_total_taxes_and_charges", this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_total_taxes_and_charges",
"base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted",
"base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount"], "base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount",
"base_paid_amount", "base_write_off_amount"],
this.frm.doc.currency != company_currency); this.frm.doc.currency != company_currency);
this.frm.toggle_display(["plc_conversion_rate", "price_list_currency"], this.frm.toggle_display(["plc_conversion_rate", "price_list_currency"],

View File

@@ -473,13 +473,11 @@ erpnext.pos.PointOfSale = Class.extend({
} }
me.frm.set_value("mode_of_payment", values.mode_of_payment); me.frm.set_value("mode_of_payment", values.mode_of_payment);
//me.frm.cscript.calculate_taxes_and_totals(); var paid_amount = flt((flt(values.paid_amount) - flt(values.change)), precision("paid_amount"));
var paid_amount = flt((flt(values.paid_amount) - flt(values.change)) / me.frm.doc.conversion_rate, precision("paid_amount"));
me.frm.set_value("paid_amount", paid_amount); me.frm.set_value("paid_amount", paid_amount);
// specifying writeoff amount here itself, so as to avoid recursion issue // specifying writeoff amount here itself, so as to avoid recursion issue
me.frm.set_value("write_off_amount", me.frm.doc.base_grand_total - paid_amount); me.frm.set_value("write_off_amount", me.frm.doc.grand_total - paid_amount);
me.frm.set_value("outstanding_amount", 0); me.frm.set_value("outstanding_amount", 0);
me.frm.savesubmit(this); me.frm.savesubmit(this);

View File

@@ -12,7 +12,15 @@ frappe.ui.form.on("Customer", "refresh", function(frm) {
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if(!frm.doc.__islocal) erpnext.utils.render_address_and_contact(frm); if(!frm.doc.__islocal) {
erpnext.utils.render_address_and_contact(frm);
}
var grid = cur_frm.get_field("sales_team").grid;
grid.set_column_disp("allocated_percentage", false);
grid.set_column_disp("allocated_amount", false);
grid.set_column_disp("incentives", false);
}) })
cur_frm.cscript.onload = function(doc, dt, dn) { cur_frm.cscript.onload = function(doc, dt, dn) {
@@ -92,11 +100,17 @@ cur_frm.fields_dict['default_price_list'].get_query = function(doc, cdt, cdn) {
cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) { cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
var filters = {
'account_type': 'Receivable',
'company': d.company,
"is_group": 0
};
if(doc.party_account_currency) {
$.extend(filters, {"account_currency": doc.party_account_currency});
}
return { return {
filters: { filters: filters
'account_type': 'Receivable',
'company': d.company,
"is_group": 0
}
} }
} }

View File

@@ -273,7 +273,7 @@
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Currency", "label": "Default Currency",
"no_copy": 1, "no_copy": 1,
"options": "Currency", "options": "Currency",
"permlevel": 0, "permlevel": 0,
@@ -463,8 +463,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,
@@ -567,7 +590,7 @@
"no_copy": 0, "no_copy": 0,
"oldfieldname": "credit_limit", "oldfieldname": "credit_limit",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "",
"permlevel": 1, "permlevel": 1,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,

View File

@@ -10,6 +10,7 @@ from frappe.utils import flt
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
from erpnext.utilities.address_and_contact import load_address_and_contact from erpnext.utilities.address_and_contact import load_address_and_contact
from erpnext.accounts.party import validate_accounting_currency, validate_party_account
from frappe.desk.reportview import build_match_conditions from frappe.desk.reportview import build_match_conditions
class Customer(TransactionBase): class Customer(TransactionBase):
@@ -27,12 +28,14 @@ class Customer(TransactionBase):
else: else:
self.name = make_autoname(self.naming_series+'.#####') self.name = make_autoname(self.naming_series+'.#####')
def validate_values(self): def validate_mandatory(self):
if frappe.defaults.get_global_default('cust_master_name') == 'Naming Series' and not self.naming_series: if frappe.defaults.get_global_default('cust_master_name') == 'Naming Series' and not self.naming_series:
frappe.throw(_("Series is mandatory"), frappe.MandatoryError) frappe.throw(_("Series is mandatory"), frappe.MandatoryError)
def validate(self): def validate(self):
self.validate_values() self.validate_mandatory()
validate_accounting_currency(self)
validate_party_account(self)
def update_lead_status(self): def update_lead_status(self):
if self.lead_name: if self.lead_name:

View File

@@ -8,6 +8,8 @@ import unittest
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from erpnext.controllers.accounts_controller import CustomerFrozen from erpnext.controllers.accounts_controller import CustomerFrozen
from erpnext.accounts.party import InvalidCurrency
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
test_ignore = ["Price List"] test_ignore = ["Price List"]
@@ -78,3 +80,13 @@ class TestCustomer(unittest.TestCase):
frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 0) frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 0)
so.save() so.save()
def test_multi_currency(self):
customer = frappe.get_doc("Customer", "_Test Customer USD")
create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
customer.party_account_currency = "EUR"
self.assertRaises(InvalidCurrency, customer.save)

View File

@@ -1,6 +1,5 @@
[ [
{ {
"company": "_Test Company",
"customer_group": "_Test Customer Group", "customer_group": "_Test Customer Group",
"customer_name": "_Test Customer", "customer_name": "_Test Customer",
"customer_type": "Individual", "customer_type": "Individual",
@@ -8,7 +7,6 @@
"territory": "_Test Territory" "territory": "_Test Territory"
}, },
{ {
"company": "_Test Company",
"customer_group": "_Test Customer Group", "customer_group": "_Test Customer Group",
"customer_name": "_Test Customer 1", "customer_name": "_Test Customer 1",
"customer_type": "Individual", "customer_type": "Individual",
@@ -16,7 +14,6 @@
"territory": "_Test Territory" "territory": "_Test Territory"
}, },
{ {
"company": "_Test Company",
"customer_group": "_Test Customer Group", "customer_group": "_Test Customer Group",
"customer_name": "_Test Customer 2", "customer_name": "_Test Customer 2",
"customer_type": "Individual", "customer_type": "Individual",
@@ -24,11 +21,22 @@
"territory": "_Test Territory" "territory": "_Test Territory"
}, },
{ {
"company": "_Test Company",
"customer_group": "_Test Customer Group", "customer_group": "_Test Customer Group",
"customer_name": "_Test Customer 3", "customer_name": "_Test Customer 3",
"customer_type": "Individual", "customer_type": "Individual",
"doctype": "Customer", "doctype": "Customer",
"territory": "_Test Territory" "territory": "_Test Territory"
},
{
"customer_group": "_Test Customer Group",
"customer_name": "_Test Customer USD",
"customer_type": "Individual",
"doctype": "Customer",
"territory": "_Test Territory",
"party_account_currency": "USD",
"accounts": [{
"company": "_Test Company",
"account": "_Test Receivable USD - _TC"
}]
} }
] ]

View File

@@ -33,31 +33,6 @@
"unique": 0, "unique": 0,
"width": "200px" "width": "200px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "sales_designation",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Designation",
"no_copy": 0,
"oldfieldname": "sales_designation",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_width": "100px",
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "100px"
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -210,7 +185,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2013-12-31 19:00:14", "modified": "2015-08-13 16:30:24.146848",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Team", "name": "Sales Team",

View File

@@ -206,30 +206,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
} }
}, },
calculate_outstanding_amount: function(update_paid_amount) {
// NOTE:
// paid_amount and write_off_amount is only for POS Invoice
// total_advance is only for non POS Invoice
if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.docstatus==0 && !this.frm.doc.is_return) {
frappe.model.round_floats_in(this.frm.doc, ["base_grand_total", "total_advance", "write_off_amount",
"paid_amount"]);
var total_amount_to_pay = this.frm.doc.base_grand_total - this.frm.doc.write_off_amount
- this.frm.doc.total_advance;
if(this.frm.doc.is_pos) {
if(!this.frm.doc.paid_amount || update_paid_amount===undefined || update_paid_amount) {
this.frm.doc.paid_amount = flt(total_amount_to_pay);
this.frm.refresh_field("paid_amount");
}
} else {
this.frm.doc.paid_amount = 0
this.frm.refresh_field("paid_amount");
}
this.frm.set_value("outstanding_amount", flt(total_amount_to_pay
- this.frm.doc.paid_amount, precision("outstanding_amount")));
}
},
calculate_commission: function() { calculate_commission: function() {
if(this.frm.fields_dict.commission_rate) { if(this.frm.fields_dict.commission_rate) {
if(this.frm.doc.commission_rate > 100) { if(this.frm.doc.commission_rate > 100) {

View File

@@ -34,13 +34,8 @@ class Company(Document):
if not self.abbr.strip(): if not self.abbr.strip():
frappe.throw(_("Abbreviation is mandatory")) frappe.throw(_("Abbreviation is mandatory"))
self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency")
if self.default_currency and self.previous_default_currency and \
self.default_currency != self.previous_default_currency and \
self.check_if_transactions_exist():
frappe.throw(_("Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."))
self.validate_default_accounts() self.validate_default_accounts()
self.validate_currency()
def validate_default_accounts(self): def validate_default_accounts(self):
for field in ["default_bank_account", "default_cash_account", "default_receivable_account", "default_payable_account", for field in ["default_bank_account", "default_cash_account", "default_receivable_account", "default_payable_account",
@@ -52,6 +47,13 @@ class Company(Document):
frappe.throw(_("Account {0} does not belong to company: {1}") frappe.throw(_("Account {0} does not belong to company: {1}")
.format(self.get(field), self.name)) .format(self.get(field), self.name))
def validate_currency(self):
self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency")
if self.default_currency and self.previous_default_currency and \
self.default_currency != self.previous_default_currency and \
self.check_if_transactions_exist():
frappe.throw(_("Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."))
def on_update(self): def on_update(self):
if not frappe.db.sql("""select name from tabAccount if not frappe.db.sql("""select name from tabAccount
where company=%s and docstatus<2 limit 1""", self.name): where company=%s and docstatus<2 limit 1""", self.name):

View File

@@ -19,7 +19,7 @@
}, },
{ {
"abbr": "_TC2", "abbr": "_TC2",
"company_name": "_Test Company 3", "company_name": "_Test Company 2",
"default_currency": "EUR", "default_currency": "EUR",
"country": "Germany", "country": "Germany",
"doctype": "Company", "doctype": "Company",

View File

@@ -10,17 +10,6 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
this._super(); this._super();
if (!doc.is_return) { if (!doc.is_return) {
if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1) {
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
var from_sales_invoice = false;
from_sales_invoice = cur_frm.doc.items.some(function(item) {
return item.against_sales_invoice ? true : false;
});
if(!from_sales_invoice)
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
}
if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1)
cur_frm.add_custom_button(__('Installation Note'), this.make_installation_note); cur_frm.add_custom_button(__('Installation Note'), this.make_installation_note);
@@ -59,7 +48,16 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
} }
} }
if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1 && !doc.is_return) {
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
var from_sales_invoice = false;
from_sales_invoice = cur_frm.doc.items.some(function(item) {
return item.against_sales_invoice ? true : false;
});
if(!from_sales_invoice)
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
}
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn); erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);

View File

@@ -307,23 +307,28 @@ class PurchaseReceipt(BuyingController):
val_rate_db_precision = 6 if cint(d.precision("valuation_rate")) <= 6 else 9 val_rate_db_precision = 6 if cint(d.precision("valuation_rate")) <= 6 else 9
# warehouse account # warehouse account
stock_value_diff = flt(flt(d.valuation_rate, val_rate_db_precision) * flt(d.qty)
* flt(d.conversion_factor), d.precision("base_net_amount"))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": warehouse_account[d.warehouse], "account": warehouse_account[d.warehouse]["name"],
"against": stock_rbnb, "against": stock_rbnb,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": flt(flt(d.valuation_rate, val_rate_db_precision) * flt(d.qty) * flt(d.conversion_factor), "debit": stock_value_diff
self.precision("base_net_amount", d)) }, warehouse_account[d.warehouse]["account_currency"]))
}))
# stock received but not billed # stock received but not billed
stock_rbnb_currency = frappe.db.get_value("Account", stock_rbnb, "account_currency")
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": stock_rbnb, "account": stock_rbnb,
"against": warehouse_account[d.warehouse], "against": warehouse_account[d.warehouse]["name"],
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(d.base_net_amount, self.precision("base_net_amount", d)) "credit": flt(d.base_net_amount, d.precision("base_net_amount")),
})) "credit_in_account_currency": flt(d.base_net_amount, d.precision("base_net_amount")) \
if stock_rbnb_currency==self.company_currency else flt(d.net_amount, d.precision("net_amount"))
}, stock_rbnb_currency))
negative_expense_to_be_booked += flt(d.item_tax_amount) negative_expense_to_be_booked += flt(d.item_tax_amount)
@@ -331,7 +336,7 @@ class PurchaseReceipt(BuyingController):
if flt(d.landed_cost_voucher_amount): if flt(d.landed_cost_voucher_amount):
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": expenses_included_in_valuation, "account": expenses_included_in_valuation,
"against": warehouse_account[d.warehouse], "against": warehouse_account[d.warehouse]["name"],
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(d.landed_cost_voucher_amount) "credit": flt(d.landed_cost_voucher_amount)
@@ -340,12 +345,12 @@ class PurchaseReceipt(BuyingController):
# sub-contracting warehouse # sub-contracting warehouse
if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": warehouse_account[self.supplier_warehouse], "account": warehouse_account[self.supplier_warehouse]["name"],
"against": warehouse_account[d.warehouse], "against": warehouse_account[d.warehouse]["name"],
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(d.rm_supp_cost) "credit": flt(d.rm_supp_cost)
})) }, warehouse_account[self.supplier_warehouse]["account_currency"]))
# divisional loss adjustment # divisional loss adjustment
sle_valuation_amount = flt(flt(d.valuation_rate, val_rate_db_precision) * flt(d.qty) * flt(d.conversion_factor), sle_valuation_amount = flt(flt(d.valuation_rate, val_rate_db_precision) * flt(d.qty) * flt(d.conversion_factor),
@@ -358,11 +363,11 @@ class PurchaseReceipt(BuyingController):
if divisional_loss: if divisional_loss:
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": stock_rbnb, "account": stock_rbnb,
"against": warehouse_account[d.warehouse], "against": warehouse_account[d.warehouse]["name"],
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": divisional_loss "debit": divisional_loss
})) }, stock_rbnb_currency))
elif d.warehouse not in warehouse_with_no_account or \ elif d.warehouse not in warehouse_with_no_account or \
d.rejected_warehouse not in warehouse_with_no_account: d.rejected_warehouse not in warehouse_with_no_account: