From e3ae05aabd6eb2ce3b0b89776164436bd6db2bf1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 9 Sep 2015 18:43:12 +0530 Subject: [PATCH] Multi-currency: Exchange Rate in Journal Entry --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 7 +- .../doctype/journal_entry/journal_entry.js | 168 ++++++---- .../doctype/journal_entry/journal_entry.json | 46 +-- .../doctype/journal_entry/journal_entry.py | 312 ++++++++++-------- .../journal_entry/test_journal_entry.py | 5 +- .../journal_entry_account.json | 103 +++++- .../purchase_invoice/purchase_invoice.py | 2 +- .../purchase_invoice/test_purchase_invoice.py | 3 +- .../purchase_invoice/test_records.json | 3 + .../doctype/sales_invoice/sales_invoice.py | 2 +- .../sales_invoice/test_sales_invoice.py | 2 +- erpnext/accounts/utils.py | 17 +- erpnext/controllers/accounts_controller.py | 4 +- erpnext/patches/v6_0/multi_currency.py | 28 +- 14 files changed, 456 insertions(+), 246 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index f89fb62bf5..dbaf5901da 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -153,17 +153,18 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga party_condition = "" # 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` where against_voucher_type=%s and against_voucher=%s and account = %s {0}""".format(party_condition), (against_voucher_type, against_voucher, account))[0][0] or 0.0) - + if against_voucher_type == 'Purchase Invoice': bal = -bal elif against_voucher_type == "Journal Entry": 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 and account = %s and ifnull(against_voucher, '') = '' {0}""" .format(party_condition), (against_voucher, account))[0][0]) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 4ed404337c..246c410fcc 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -25,16 +25,29 @@ frappe.ui.form.on("Journal Entry", { // 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 = ["debit_in_account_currency", "credit_in_account_currency"]; - - var company_currency = erpnext.get_currency(frm.doc.company); - + var fields = ["currency_section", "account_currency", "exchange_rate", "debit", "credit"]; + var grid = frm.get_field("accounts").grid; - grid.set_column_disp(fields, grid.account_currency!=company_currency); + 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({ @@ -62,17 +75,26 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ setup_queries: function() { var me = this; - - $.each(["account", "cost_center"], function(i, fieldname) { - me.frm.set_query(fieldname, "accounts", function() { - frappe.model.validate_missing(me.frm.doc, "company"); - return { - filters: { - company: me.frm.doc.company, - is_group: 0 - } - }; - }); + var company_currency = erpnext.get_currency(me.frm.doc.company); + + me.frm.set_query("account", "accounts", function(doc, cdt, cdn) { + var filters = { + company: me.frm.doc.company, + is_group: 0 + }; + if(!doc.multi_currency) { + $.extend(filters, {currency: company_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) { @@ -151,32 +173,39 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ reference_name: function(doc, cdt, cdn) { var d = frappe.get_doc(cdt, cdn); - if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) { - this.get_outstanding('Purchase Invoice', d.reference_name, d); - } - if (d.reference_type==="Sales Invoice" && !flt(d.credit)) { - this.get_outstanding('Sales Invoice', d.reference_name, 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_name) { + 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, doc.company, 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 args = { "doctype": doctype, "docname": docname, "party": child.party, - "account": child.account + "account": child.account, + "account_currency": child.account_currency, + "company": company } - return this.frm.call({ - child: child, - method: "get_outstanding", + return frappe.call({ + method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_outstanding", args: { args: args}, 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); + }) + } } }); }, @@ -325,42 +354,26 @@ frappe.ui.form.on("Journal Entry Account", { account: d.account, date: frm.doc.posting_date, company: frm.doc.company, - credited: flt(d.credit_in_account_currency) > 0 ? true : false + 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[0]); - refresh_field('balance', d.name, 'accounts'); - refresh_field('party_type', d.name, 'accounts'); - refresh_field('account_currency', d.name, 'accounts'); - - if(r.message[1] && (!frm.doc.exchange_rate || frm.doc.exchange_rate == 1.0)) { - frm.set_value("exchange_rate", r.message[1]) - } + $.extend(d, r.message); + refresh_field('accounts'); } } }); } }, - debit_in_account_currency: function(frm, dt, dn) { - var company_currency = erpnext.get_currency(frm.doc.company); - var row = locals[dt][dn]; - - var exchange_rate = (row.account_currency==company_currency) ? 1 : frm.doc.exchange_rate; - - frappe.model.set_value(dt, dn, "debit", - flt(flt(row.debit_in_account_currency)*exchange_rate), precision("debit", row)); + 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, dt, dn) { - var company_currency = erpnext.get_currency(frm.doc.company); - var row = locals[dt][dn]; - - var exchange_rate = (row.account_currency==company_currency) ? 1 : frm.doc.exchange_rate; - - frappe.model.set_value(dt, dn, "credit", - flt(flt(row.credit_in_account_currency)*exchange_rate), precision("credit", row)); + 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) { @@ -369,9 +382,52 @@ frappe.ui.form.on("Journal Entry Account", { credit: function(frm, dt, dn) { cur_frm.cscript.update_totals(frm.doc); + }, + + exchange_rate: function(frm, cdt, cdn) { + erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn); } }) frappe.ui.form.on("Journal Entry Account", "accounts_remove", function(frm) { cur_frm.cscript.update_totals(frm.doc); -}); \ No newline at end of file +}); + +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 = erpnext.get_currency(frm.doc.company); + 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: row.reference_type, + reference_name: 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); + } + } + }) + } +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index ea632f1266..af49d68f14 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -147,28 +147,6 @@ "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": 1, - "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, @@ -422,6 +400,28 @@ "set_only_once": 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, "bold": 0, @@ -1024,7 +1024,7 @@ "is_submittable": 1, "issingle": 0, "istable": 0, - "modified": "2015-08-27 16:07:33.265318", + "modified": "2015-09-09 02:07:40.980884", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 3db1820cbf..04324c0efd 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -5,9 +5,9 @@ from __future__ import unicode_literals import frappe from frappe.utils import cstr, flt, fmt_money, formatdate from frappe import msgprint, _, scrub -from erpnext.setup.utils import get_company_currency, get_exchange_rate from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.utils import get_balance_on +from erpnext.setup.utils import get_company_currency class JournalEntry(AccountsController): @@ -267,39 +267,37 @@ class JournalEntry(AccountsController): def validate_multi_currency(self): alternate_currency = [] for d in self.get("accounts"): - d.account_currency = frappe.db.get_value("Account", d.account, "account_currency") or self.company_currency + 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 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() - - if not self.exchange_rate: - frappe.throw(_("Exchange Rate is mandatory in multi-currency Journal Entry")) - else: - self.exchange_rate = 1.0 - + + self.set_exchange_rate() + for d in self.get("accounts"): - exchange_rate = self.exchange_rate if d.account_currency != self.company_currency else 1 - - d.debit = flt(flt(d.debit_in_account_currency)*exchange_rate, d.precision("debit")) - d.credit = flt(flt(d.credit_in_account_currency)*exchange_rate, d.precision("credit")) - + 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: - account_type = frappe.db.get_value("Account", d.account, "account_type") - if account_type == "Bank" and flt(d.credit_in_account_currency) > 0: - self.exchange_rate = get_average_exchange_rate(d.account) - break - if not self.exchange_rate: - self.exchange_rate = get_exchange_rate(d.account_currency, self.company_currency) - - + 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): r = [] @@ -387,21 +385,21 @@ class JournalEntry(AccountsController): diff = flt(self.difference, self.precision("difference")) # If any row without amount, set the diff on that row - for d in self.get('accounts'): - if not d.credit and not d.debit and diff != 0: - if diff>0: - d.credit = diff - elif diff<0: - d.debit = diff - flag = 1 - - # Set the diff in a new row - if flag == 0 and diff != 0: - jd = self.append('accounts', {}) + if diff: + for d in self.get('accounts'): + if not d.credit_in_account_currency and not d.debit_in_account_currency and diff != 0: + blank_row = d + + if not blank_row: + blank_row = self.append('accounts', {}) + + blank_row.exchange_rate = 1 if diff>0: - jd.credit = abs(diff) + blank_row.credit_in_account_currency = diff + blank_row.credit = diff 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() @@ -500,10 +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}) if account: + account_details = frappe.db.get_value("Account", account, ["account_currency", "account_type"], as_dict=1) return { "account": account, "balance": get_balance_on(account), - "account_currency": frappe.db.get_value("Account", account, "account_currency") + "account_currency": account_details.account_currency, + "account_type": account_details.account_type } @frappe.whitelist() @@ -513,31 +513,36 @@ def get_payment_entry_from_sales_invoice(sales_invoice): si = frappe.get_doc("Sales Invoice", sales_invoice) # exchange rate - if si.company_currency == si.party_account_currency: - exchange_rate = 1 - else: - exchange_rate = get_exchange_rate(si.party_account_currency, si.company_currency) + exchange_rate = get_exchange_rate(si.debit_to, si.party_account_currency, si.company, + si.doctype, si.name) jv = get_payment_entry(si) jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks) - jv.exchange_rate = exchange_rate # credit customer - jv.get("accounts")[0].account = si.debit_to - jv.get("accounts")[0].account_currency = si.party_account_currency - jv.get("accounts")[0].party_type = "Customer" - jv.get("accounts")[0].party = si.customer - jv.get("accounts")[0].balance = get_balance_on(si.debit_to) - jv.get("accounts")[0].party_balance = get_balance_on(party=si.customer, party_type="Customer") - jv.get("accounts")[0].credit_in_account_currency = si.outstanding_amount - jv.get("accounts")[0].reference_type = si.doctype - jv.get("accounts")[0].reference_name = si.name + row1 = jv.get("accounts")[0] + row1.account = si.debit_to + row1.account_currency = si.party_account_currency + row1.party_type = "Customer" + row1.party = si.customer + row1.balance = get_balance_on(si.debit_to) + row1.party_balance = get_balance_on(party=si.customer, party_type="Customer") + 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 - if jv.get("accounts")[1].account_currency == si.party_account_currency: - jv.get("accounts")[1].debit_in_account_currency = 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: - jv.get("accounts")[1].debit_in_account_currency = si.outstanding_amount * exchange_rate + 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() @@ -546,31 +551,37 @@ def get_payment_entry_from_purchase_invoice(purchase_invoice): """Returns new Journal Entry document as dict for given Purchase Invoice""" pi = frappe.get_doc("Purchase Invoice", purchase_invoice) - if pi.company_currency == pi.party_account_currency: - exchange_rate = 1 - else: - exchange_rate = get_exchange_rate(pi.party_account_currency, pi.company_currency) + exchange_rate = get_exchange_rate(pi.debit_to, pi.party_account_currency, pi.company, + pi.doctype, pi.name) jv = get_payment_entry(pi) jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks) jv.exchange_rate = exchange_rate # credit supplier - jv.get("accounts")[0].account = pi.credit_to - jv.get("accounts")[0].account_currency = pi.party_account_currency - jv.get("accounts")[0].party_type = "Supplier" - jv.get("accounts")[0].party = pi.supplier - jv.get("accounts")[0].balance = get_balance_on(pi.credit_to) - jv.get("accounts")[0].party_balance = get_balance_on(party=pi.supplier, party_type="Supplier") - jv.get("accounts")[0].debit_in_account_currency = pi.outstanding_amount - jv.get("accounts")[0].reference_type = pi.doctype - jv.get("accounts")[0].reference_name = pi.name + row1 = jv.get("accounts")[0] + row1.account = pi.credit_to + row1.account_currency = pi.party_account_currency + row1.party_type = "Supplier" + row1.party = pi.supplier + row1.balance = get_balance_on(pi.credit_to) + row1.party_balance = get_balance_on(party=pi.supplier, party_type="Supplier") + 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 - if jv.get("accounts")[1].account_currency == pi.party_account_currency: - jv.get("accounts")[1].credit_in_account_currency = 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: - jv.get("accounts")[1].credit_in_account_currency = pi.outstanding_amount * exchange_rate + 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() @@ -590,37 +601,39 @@ def get_payment_entry_from_sales_order(sales_order): party_account = get_party_account(so.company, so.customer, "Customer") party_account_currency = frappe.db.get_value("Account", party_account, "account_currency") - company_currency = get_company_currency(so.company) - if so.company_currency == party_account_currency: - exchange_rate = 1 - else: - exchange_rate = get_exchange_rate(party_account_currency, so.company_currency) - - jv.exchange_rate = exchange_rate - - if party_account_currency == company_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 - jv.get("accounts")[0].account = party_account - jv.get("accounts")[0].account_currency = party_account_currency - jv.get("accounts")[0].party_type = "Customer" - jv.get("accounts")[0].party = so.customer - jv.get("accounts")[0].balance = get_balance_on(party_account) - jv.get("accounts")[0].party_balance = get_balance_on(party=so.customer, party_type="Customer") - jv.get("accounts")[0].credit_in_account_currency = amount - jv.get("accounts")[0].reference_type = so.doctype - jv.get("accounts")[0].reference_name = so.name - jv.get("accounts")[0].is_advance = "Yes" + row1 = jv.get("accounts")[0] + row1.account = party_account + row1.account_currency = party_account_currency + row1.party_type = "Customer" + row1.party = so.customer + row1.balance = get_balance_on(party_account) + row1.party_balance = get_balance_on(party=so.customer, party_type="Customer") + row1.credit_in_account_currency = amount + 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 - if jv.get("accounts")[1].account_currency == party_account_currency: - jv.get("accounts")[1].debit_in_account_currency = amount + row2 = jv.get("accounts")[1] + if row2.account_currency == party_account_currency: + row2.debit_in_account_currency = amount else: - jv.get("accounts")[1].debit_in_account_currency = amount * exchange_rate + 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() @@ -639,36 +652,38 @@ def get_payment_entry_from_purchase_order(purchase_order): party_account = get_party_account(po.company, po.supplier, "Supplier") party_account_currency = frappe.db.get_value("Account", party_account, "account_currency") - company_currency = get_company_currency(po.company) - if po.company_currency == party_account_currency: - exchange_rate = 1 - else: - exchange_rate = get_exchange_rate(party_account_currency, po.company_currency) + exchange_rate = get_exchange_rate(party_account, party_account_currency, po.company) - jv.exchange_rate = exchange_rate - - if party_account_currency == company_currency: + 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 - jv.get("accounts")[0].account = party_account - jv.get("accounts")[0].party_type = "Supplier" - jv.get("accounts")[0].party = po.supplier - jv.get("accounts")[0].balance = get_balance_on(party_account) - jv.get("accounts")[0].party_balance = get_balance_on(party=po.supplier, party_type="Supplier") - jv.get("accounts")[0].debit_in_account_currency = amount - jv.get("accounts")[0].reference_type = po.doctype - jv.get("accounts")[0].reference_name = po.name - jv.get("accounts")[0].is_advance = "Yes" + row1 = jv.get("accounts")[0] + row1.account = party_account + row1.party_type = "Supplier" + row1.party = po.supplier + row1.balance = get_balance_on(party_account) + row1.party_balance = get_balance_on(party=po.supplier, party_type="Supplier") + row1.debit_in_account_currency = amount + row1.reference_type = po.doctype + row1.reference_name = po.name + row1.is_advance = "Yes" + row1.exchange_rate = exchange_rate + row1.account_type = "Payable" # debit bank - if jv.get("accounts")[1].account_currency == party_account_currency: - jv.get("accounts")[1].credit_in_account_currency = amount + row2 = jv.get("accounts")[1] + if row2.account_currency == party_account_currency: + row2.credit_in_account_currency = amount else: - jv.get("accounts")[1].credit_in_account_currency = amount * exchange_rate + 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() @@ -687,6 +702,9 @@ def get_payment_entry(doc): d2.account = bank_account["account"] 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 @@ -712,27 +730,37 @@ def get_outstanding(args): if not frappe.has_permission("Account"): frappe.msgprint(_("No Permission"), raise_exception=1) args = eval(args) + company_currency = get_company_currency(args.get("company")) + if args.get("doctype") == "Journal Entry": condition = " and party=%(party)s" if args.get("party") else "" 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} and ifnull(reference_type, '')=''""".format(condition), args) 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 { - ("credit" if against_jv_amount > 0 else "debit"): abs(against_jv_amount) + amount_field: abs(against_jv_amount) } - elif args.get("doctype") == "Sales Invoice": - outstanding_amount = flt(frappe.db.get_value("Sales Invoice", args["docname"], "outstanding_amount")) + elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"): + 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 { - ("credit" if outstanding_amount > 0 else "debit"): abs(outstanding_amount) - } - 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) + amount_field: abs(flt(invoice.outstanding_amount)), + "exchange_rate": exchange_rate } @frappe.whitelist() @@ -753,7 +781,7 @@ def get_party_account_and_balance(company, party_type, party): } @frappe.whitelist() -def get_account_balance_and_party_type(account, date, company, credited=False): +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.""" if not frappe.has_permission("Account"): frappe.msgprint(_("No Permission"), raise_exception=1) @@ -768,19 +796,37 @@ def get_account_balance_and_party_type(account, date, company, credited=False): else: party_type = "" - exchange_rate = None - if account_details.account_currency != company_currency: - if account_details.account_type == "Bank" and credited: - exchange_rate = get_average_exchange_rate(account) - else: - exchange_rate = get_exchange_rate(account_details.account_currency, company_currency) - 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, 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 diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 0d9cd249ab..753e0126c9 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -218,19 +218,20 @@ def make_journal_entry(account1, account2, amount, cost_center=None, exchange_ra jv.company = "_Test Company" jv.fiscal_year = "_Test Fiscal Year 2013" jv.user_remark = "test" - jv.exchange_rate = exchange_rate - + jv.multi_currency = 1 jv.set("accounts", [ { "account": account1, "cost_center": cost_center, "debit_in_account_currency": amount if amount > 0 else 0, "credit_in_account_currency": abs(amount) if amount < 0 else 0, + "exchange_rate": exchange_rate }, { "account": account2, "cost_center": cost_center, "credit_in_account_currency": amount if amount > 0 else 0, "debit_in_account_currency": abs(amount) if amount < 0 else 0, + exchange_rate: exchange_rate } ]) if save or submit: diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index 85f410982c..577df2510c 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -38,19 +38,18 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "account_currency", - "fieldtype": "Link", + "fieldname": "account_type", + "fieldtype": "Data", "hidden": 1, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Account Currency", - "no_copy": 1, - "options": "Currency", + "label": "Account Type", + "no_copy": 0, "permlevel": 0, "precision": "", "print_hide": 1, - "read_only": 1, + "read_only": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -196,6 +195,96 @@ "set_only_once": 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, "bold": 0, @@ -474,7 +563,7 @@ "is_submittable": 0, "issingle": 0, "istable": 1, - "modified": "2015-09-04 03:29:37.127968", + "modified": "2015-09-09 12:55:59.270539", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 4fac12129e..d92e1fa0cc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -216,7 +216,7 @@ class PurchaseInvoice(BuyingController): 'party_type': 'Supplier', 'party': self.supplier, 'is_advance' : 'Yes', - 'dr_or_cr' : 'debit', + 'dr_or_cr' : 'debit_in_account_currency', 'unadjusted_amt' : flt(d.advance_amount), 'allocated_amt' : flt(d.allocated_amount) } diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 2754ee6def..b39f30bbdc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -219,7 +219,8 @@ class TestPurchaseInvoice(unittest.TestCase): pi.load_from_db() 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) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_records.json b/erpnext/accounts/doctype/purchase_invoice/test_records.json index 679e87024a..e6961d978b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_records.json +++ b/erpnext/accounts/doctype/purchase_invoice/test_records.json @@ -141,6 +141,9 @@ "supplier": "_Test Supplier", "supplier_name": "_Test Supplier" }, + + + { "bill_no": "NA", "buying_price_list": "_Test Price List", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index bce8fe214e..3f368184f2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -260,7 +260,7 @@ class SalesInvoice(SellingController): 'party_type': 'Customer', 'party': self.customer, 'is_advance' : 'Yes', - 'dr_or_cr' : 'credit', + 'dr_or_cr' : 'credit_in_account_currency', 'unadjusted_amt' : flt(d.advance_amount), 'allocated_amt' : flt(d.allocated_amount) } diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index e519bd3969..449f98d8f2 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -661,7 +661,7 @@ class TestSalesInvoice(unittest.TestCase): where reference_name=%s""", si.name)) 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) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index f0f096e9c3..89ff6cff15 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -198,21 +198,26 @@ def update_against_doc(d, jv_obj): jv_detail.set("reference_name", d["against_voucher"]) if d['allocated_amt'] < d['unadjusted_amt']: - jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance - from `tabJournal Entry Account` where name = %s""", d['voucher_detail_no']) + jvd = frappe.db.sql(""" + 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 ch = jv_obj.append("accounts") 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 = d["party"] - ch.cost_center = cstr(jvd[0][0]) - ch.balance = flt(jvd[0][1]) + ch.cost_center = cstr(jvd[0]["cost_center"]) + 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']== '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_name = original_reference_name - ch.is_advance = cstr(jvd[0][3]) + ch.is_advance = cstr(jvd[0]["is_advance"]) ch.docstatus = 1 # will work as update after submit diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 47d7e7a4fe..39527df97a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -236,8 +236,8 @@ class AccountsController(TransactionBase): .format(account, " or ".join(valid_currency))) def set_balance_in_account_currency(self, gl_dict, account_currency=None): - if not (self.get("conversion_rate") or self.get("exchange_rate")) \ - and account_currency!=self.company_currency: + 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)) diff --git a/erpnext/patches/v6_0/multi_currency.py b/erpnext/patches/v6_0/multi_currency.py index 793a0c2170..2b53134c05 100644 --- a/erpnext/patches/v6_0/multi_currency.py +++ b/erpnext/patches/v6_0/multi_currency.py @@ -37,18 +37,18 @@ def execute(): """, (company.default_currency, company.name)) # update exchange rate, debit/credit in account currency in Journal Entry - frappe.db.sql("""update `tabJournal Entry` set exchange_rate=1""") - frappe.db.sql(""" - update - `tabJournal Entry Account` jea, `tabJournal Entry` je - set + update `tabJournal Entry Account` jea + set exchange_rate=1, debit_in_account_currency=debit, credit_in_account_currency=credit, - account_currency=%s - where - jea.parent = je.name - and je.company=%s + 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 @@ -87,7 +87,15 @@ def execute(): break if not party_account_exists: - party_account = party_gle.account if party_gle else company.default_receivable_account + 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,