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
commit aaf1895a12
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.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
@ -166,6 +166,30 @@
"set_only_once": 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,
"bold": 0,

View File

@ -30,6 +30,7 @@ class Account(Document):
self.validate_warehouse_account()
self.validate_frozen_accounts_modifier()
self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency()
def validate_parent(self):
"""Fetch Parent Details and validate parent account"""
@ -88,6 +89,14 @@ class Account(Document):
frappe.throw(_("Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"))
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'"))
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):
if self.check_if_child_exists():

View File

@ -36,7 +36,8 @@ def create_charts(chart_name, company):
"is_group": is_group,
"root_type": root_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:

View File

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

View File

@ -156,7 +156,7 @@
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Debit Amt",
"label": "Debit Amount",
"no_copy": 0,
"oldfieldname": "debit",
"oldfieldtype": "Currency",
@ -180,7 +180,7 @@
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Credit Amt",
"label": "Credit Amount",
"no_copy": 0,
"oldfieldname": "credit",
"oldfieldtype": "Currency",
@ -194,6 +194,75 @@
"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": 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,
"bold": 0,

View File

@ -10,6 +10,8 @@ from frappe import _
from frappe.model.document import Document
class CustomerFrozen(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
class GLEntry(Document):
def validate(self):
@ -20,6 +22,7 @@ class GLEntry(Document):
self.check_pl_account()
self.validate_cost_center()
self.validate_party()
self.validate_currency()
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
self.validate_account_details(adv_adj)
@ -98,6 +101,25 @@ class GLEntry(Document):
if not frozen_accounts_modifier in frappe.get_roles():
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)
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):
if not adv_adj and account:
@ -131,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])

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")
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.flags.ignore_validate = True

View File

@ -2,8 +2,54 @@
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.accounts");
frappe.provide("erpnext.journal_entry");
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({
onload: function() {
this.load_defaults();
@ -29,17 +75,27 @@ 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
}
};
});
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, {
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) {
@ -118,32 +174,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);
})
}
}
});
},
@ -161,35 +224,20 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// set difference
if(doc.difference) {
if(doc.difference > 0) {
row.credit_in_account_currency = doc.difference;
row.credit = doc.difference;
} else {
row.debit_in_account_currency = -doc.difference;
row.debit = -doc.difference;
}
}
cur_frm.cscript.update_totals(doc);
},
});
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.refresh_fields();
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) {
var td=0.0; var tc =0.0;
var el = doc.accounts || [];
for(var i in el) {
td += flt(el[i].debit, precision("debit", el[i]));
tc += flt(el[i].credit, precision("credit", el[i]));
var accounts = doc.accounts || [];
for(var i in accounts) {
td += flt(accounts[i].debit, precision("debit", accounts[i]));
tc += flt(accounts[i].credit, precision("credit", accounts[i]));
}
var doc = locals[doc.doctype][doc.name];
doc.total_debit = td;
@ -213,32 +261,12 @@ cur_frm.cscript.update_totals = function(doc) {
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.update_totals(doc);
return $c_obj(cur_frm.doc, 'get_balance', '', function(r, rt){
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.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) {
var d = frappe.get_doc(cdt, cdn);
if(!d.account && d.party_type && d.party) {
return frm.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance",
child: d,
args: {
company: frm.doc.company,
party_type: d.party_type,
party: d.party
}
});
frappe.ui.form.on("Journal Entry Account", {
party: function(frm, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn);
if(!d.account && d.party_type && d.party) {
return frm.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance",
child: d,
args: {
company: frm.doc.company,
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);
});
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);
}
}
})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,11 +3,11 @@
from __future__ import unicode_literals
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 erpnext.setup.utils import get_company_currency
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):
@ -26,6 +26,7 @@ class JournalEntry(AccountsController):
self.validate_party()
self.validate_cheque_info()
self.validate_entries_for_advance()
self.validate_multi_currency()
self.validate_debit_and_credit()
self.validate_against_jv()
self.validate_reference_doc()
@ -35,6 +36,7 @@ class JournalEntry(AccountsController):
self.validate_expense_claim()
self.validate_credit_debit_note()
self.validate_empty_accounts_table()
self.set_account_and_party_balance()
self.set_title()
def on_submit(self):
@ -144,6 +146,7 @@ class JournalEntry(AccountsController):
self.reference_totals = {}
self.reference_types = {}
self.reference_parties = {}
for d in self.get("accounts"):
if not d.reference_type:
@ -151,8 +154,8 @@ class JournalEntry(AccountsController):
if not d.reference_name:
d.reference_type = None
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") \
else "debit"
dr_or_cr = "credit_in_account_currency" \
if d.reference_type in ("Sales Order", "Sales Invoice") else "debit_in_account_currency"
# check debit or credit type Sales / Purchase Order
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] += flt(d.get(dr_or_cr))
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,
[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"""
for reference_name, total in self.reference_totals.iteritems():
reference_type = self.reference_types[reference_name]
party_type, party = self.reference_parties.get(reference_name)
if reference_type in ("Sales Order", "Purchase Order"):
voucher_properties = frappe.db.get_value(reference_type, reference_name,
["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"])
order = frappe.db.get_value(reference_type, reference_name,
["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))
if flt(voucher_properties[1]) >= 100:
if flt(order.per_billed) >= 100:
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))
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 \
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):
"""Validate totals and docstatus for invoices"""
@ -215,15 +228,15 @@ class JournalEntry(AccountsController):
reference_type = self.reference_types[reference_name]
if reference_type in ("Sales Invoice", "Purchase Invoice"):
voucher_properties = frappe.db.get_value(reference_type, reference_name,
["docstatus", "outstanding_amount"])
invoice = frappe.db.get_value(reference_type, reference_name,
["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))
if total and flt(voucher_properties[1]) < total:
frappe.throw(_("Payment against {0} {1} cannot be greater \
than Outstanding Amount {2}").format(reference_type, reference_name, voucher_properties[1]))
if total and flt(invoice.outstanding_amount) < total:
frappe.throw(_("Payment against {0} {1} cannot be greater than Outstanding Amount {2}")
.format(reference_type, reference_name, invoice.outstanding_amount))
def set_against_account(self):
accounts_debited, accounts_credited = [], []
@ -237,13 +250,12 @@ class JournalEntry(AccountsController):
def validate_debit_and_credit(self):
self.total_debit, self.total_credit, self.difference = 0, 0, 0
for d in self.get("accounts"):
if d.debit and d.credit:
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_credit = flt(self.total_credit) + flt(d.credit, self.precision("credit", "accounts"))
self.total_debit = flt(self.total_debit) + flt(d.debit, d.precision("debit"))
self.total_credit = flt(self.total_credit) + flt(d.credit, d.precision("credit"))
self.difference = flt(self.total_debit, self.precision("total_debit")) - \
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}")
.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):
r = []
if self.cheque_no:
@ -260,15 +307,13 @@ class JournalEntry(AccountsController):
else:
msgprint(_("Please enter Reference date"), raise_exception=frappe.MandatoryError)
company_currency = get_company_currency(self.company)
for d in self.get('accounts'):
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))
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))
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)
if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
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'))))
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))
if self.user_remark:
@ -301,10 +346,9 @@ class JournalEntry(AccountsController):
self.set_total_amount(d.debit or d.credit)
def set_total_amount(self, amt):
company_currency = get_company_currency(self.company)
self.total_amount = amt
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):
from erpnext.accounts.general_ledger import make_gl_entries
@ -318,8 +362,11 @@ class JournalEntry(AccountsController):
"party_type": d.party_type,
"party": d.party,
"against": d.against_account,
"debit": flt(d.debit, self.precision("debit", "accounts")),
"credit": flt(d.credit, self.precision("credit", "accounts")),
"debit": flt(d.debit, d.precision("debit")),
"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": d.reference_name,
"remarks": self.remark,
@ -338,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
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
# Set the diff in a new row
if flag == 0 and diff != 0:
jd = self.append('accounts', {})
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()
@ -427,6 +474,11 @@ class JournalEntry(AccountsController):
if not self.get('accounts'):
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()
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
@ -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})
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)
"balance": get_balance_on(account),
"account_currency": account_details.account_currency,
"account_type": account_details.account_type
}
@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"""
from erpnext.accounts.utils import get_balance_on
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.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks)
# credit customer
jv.get("accounts")[0].account = si.debit_to
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 = 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
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()
@ -478,21 +550,38 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
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)
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].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 = 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
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()
@ -501,6 +590,7 @@ def get_payment_entry_from_sales_order(sales_order):
"""Returns new Journal Entry document as dict for given Sales Order"""
from erpnext.accounts.utils import get_balance_on
from erpnext.accounts.party import get_party_account
so = frappe.get_doc("Sales Order", sales_order)
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.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
jv.get("accounts")[0].account = party_account
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 = 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
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()
@ -540,23 +649,41 @@ def get_payment_entry_from_purchase_order(purchase_order):
jv = get_payment_entry(po)
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
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 = 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
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()
@ -574,6 +701,10 @@ def get_payment_entry(doc):
if bank_account:
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
@ -599,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()
@ -640,14 +781,58 @@ def get_party_account_and_balance(company, party_type, party):
}
@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."""
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
account_type = frappe.db.get_value("Account", account, "account_type")
return {
"balance": get_balance_on(account, date),
"party_type": {"Receivable":"Customer", "Payable":"Supplier"}.get(account_type, "")
}
company_currency = get_company_currency(company)
account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
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")
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",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
@ -112,7 +112,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28")
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)
@ -126,7 +126,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28")
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)
@ -136,13 +136,13 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28")
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",
{"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
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",
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
@ -165,32 +165,80 @@ class TestJournalEntry(unittest.TestCase):
def set_total_expense_zero(self, posting_date):
existing_expense = self.get_actual_expense(posting_date)
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 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)
def make_journal_entry(account1, account2, amount, cost_center=None, submit=False):
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.posting_date = "2013-02-14"
jv.company = "_Test Company"
jv.fiscal_year = "_Test Fiscal Year 2013"
jv.user_remark = "test"
jv.multi_currency = 1
jv.set("accounts", [
{
"account": account1,
"cost_center": cost_center,
"debit": amount if amount > 0 else 0,
"credit": abs(amount) if amount < 0 else 0,
"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": amount if amount > 0 else 0,
"debit": abs(amount) if amount < 0 else 0,
"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
}
])
jv.insert()
if save or submit:
jv.insert()
if submit:
jv.submit()
if submit:
jv.submit()
return jv

View File

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

View File

@ -34,6 +34,28 @@
"unique": 0,
"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,
"bold": 0,
@ -48,7 +70,7 @@
"no_copy": 1,
"oldfieldname": "balance",
"oldfieldtype": "Data",
"options": "Company:company:default_currency",
"options": "account_currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
@ -162,7 +184,7 @@
"in_list_view": 0,
"label": "Party Balance",
"no_copy": 0,
"options": "Company:company:default_currency",
"options": "account_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -173,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,
@ -198,20 +310,43 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "debit",
"fieldname": "debit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Debit",
"label": "Debit in Account Currency",
"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",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@ -242,20 +377,43 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "credit",
"fieldname": "credit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Credit",
"label": "Credit in Account Currency",
"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",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@ -405,7 +563,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2015-08-17 02:11:33.991361",
"modified": "2015-09-09 12:55:59.270539",
"modified_by": "Administrator",
"module": "Accounts",
"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) {
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);
});
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
frappe.ui.form.on("Payment Tool", "payment_mode", function(frm) {
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',
args: {
"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) {
if(!r.exc) {
@ -216,4 +233,4 @@ erpnext.payment_tool.check_mandatory_to_fetch = function(doc) {
$.each(["Company", "Party Type", "Party", "Received or Paid"], function(i, field) {
if(!doc[frappe.model.scrub(field)]) frappe.throw(__("Please select {0} first", [field]));
});
}
}

View File

@ -106,7 +106,7 @@
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Column Break 1",
"label": "",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
@ -162,6 +162,29 @@
"set_only_once": 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,
"bold": 0,
@ -306,6 +329,7 @@
"in_list_view": 0,
"label": "Total Payment Amount",
"no_copy": 0,
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
@ -450,7 +474,7 @@
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2015-06-05 11:17:33.843334",
"modified": "2015-08-31 18:58:21.813054",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Tool",

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe import _, scrub
from frappe.utils import flt
from frappe.model.document import Document
import json
@ -33,15 +33,18 @@ class PaymentTool(Document):
d1.party_type = self.party_type
d1.party = self.party
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_name", v.against_voucher_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.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:
d2.balance = get_balance_on(self.payment_account)
@ -55,11 +58,14 @@ def get_outstanding_vouchers(args):
frappe.throw(_("No permission to use Payment Tool"), frappe.PermissionError)
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":
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":
amount_query = "ifnull(credit, 0) - ifnull(debit, 0)"
amount_query = "ifnull(credit_in_account_currency, 0) - ifnull(debit_in_account_currency, 0)"
else:
frappe.throw(_("Please enter the Against Vouchers manually"))
@ -68,27 +74,34 @@ def get_outstanding_vouchers(args):
args.get("party_type"), args.get("party"))
# 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
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'
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
orders = frappe.db.sql("""
select
name as voucher_no,
ifnull(base_grand_total, 0) as invoice_amount,
(ifnull(base_grand_total, 0) - ifnull(advance_paid, 0)) as outstanding_amount,
ifnull({ref_field}, 0) as invoice_amount,
(ifnull({ref_field}, 0) - ifnull(advance_paid, 0)) as outstanding_amount,
transaction_date as posting_date
from
`tab%s`
`tab{voucher_type}`
where
%s = %s
{party_type} = %s
and docstatus = 1
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
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'),
party, as_dict = True)
""".format(**{
"ref_field": ref_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type)
}), party, as_dict = True)
order_list = []
for d in orders:
@ -98,13 +111,19 @@ def get_orders_to_be_billed(party_type, party):
return order_list
@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"]:
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"]:
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":
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"""
.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",
"reference_type": "Sales Order",
"reference_name": so2.name,
"credit": 1000,
"credit_in_account_currency": 1000,
"is_advance": "Yes"
})
@ -67,7 +67,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3",
"reference_type": si2.doctype,
"reference_name": si2.name,
"credit": 561.80
"credit_in_account_currency": 561.80
})
pi = self.create_voucher(pi_test_records[0], {
@ -91,7 +91,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3",
"party_account": "_Test Receivable - _TC",
"payment_mode": "Cheque",
"payment_account": "_Test Account Bank Account - _TC",
"payment_account": "_Test Bank - _TC",
"reference_no": "123456",
"reference_date": "2013-02-14"
}
@ -117,10 +117,10 @@ class TestPaymentTool(unittest.TestCase):
def create_against_jv(self, test_record, args):
jv = frappe.copy_doc(test_record)
jv.get("accounts")[0].update(args)
if args.get("debit"):
jv.get("accounts")[1].credit = args["debit"]
elif args.get("credit"):
jv.get("accounts")[1].debit = args["credit"]
if args.get("debit_in_account_currency"):
jv.get("accounts")[1].credit_in_account_currency = args["debit_in_account_currency"]
elif args.get("credit_in_account_currency"):
jv.get("accounts")[1].debit_in_account_currency = args["credit_in_account_currency"]
jv.insert()
jv.submit()
@ -141,7 +141,8 @@ class TestPaymentTool(unittest.TestCase):
outstanding_entries = get_outstanding_vouchers(json.dumps(args))
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)
@ -156,11 +157,10 @@ class TestPaymentTool(unittest.TestCase):
paytool.total_payment_amount = 300
new_jv = paytool.make_journal_entry()
for jv_entry in new_jv.get("accounts"):
if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"):
self.assertEquals(100.00,
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"))
self.assertEquals(100.00, jv_entry.get("debit_in_account_currency"
if paytool.party_type=="Supplier" else "credit_in_account_currency"))
self.assertEquals(jv_entry.reference_name,
expected_outstanding[jv_entry.reference_type][0])
@ -170,4 +170,6 @@ class TestPaymentTool(unittest.TestCase):
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 `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 Invoice` where supplier = "_Test Supplier 1" """)

View File

@ -87,6 +87,7 @@
"in_list_view": 1,
"label": "Total Amount",
"no_copy": 0,
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
@ -108,6 +109,7 @@
"in_list_view": 1,
"label": "Outstanding Amount",
"no_copy": 0,
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
@ -129,6 +131,7 @@
"in_list_view": 1,
"label": "Payment Amount",
"no_copy": 0,
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
@ -146,7 +149,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2014-09-11 08:55:34.384017",
"modified": "2015-08-31 18:58:35.537060",
"modified_by": "Administrator",
"module": "Accounts",
"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):
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)
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
from `tabGL Entry` t1, `tabAccount` t2

View File

@ -75,8 +75,29 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
me.apply_pricing_rule();
})
},
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() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.calculate_outstanding_amount();
this.frm.refresh_fields();
},

View File

@ -1266,30 +1266,6 @@
"set_only_once": 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,
"bold": 0,
@ -1304,7 +1280,7 @@
"no_copy": 1,
"oldfieldname": "total_advance",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
@ -1328,7 +1304,7 @@
"no_copy": 1,
"oldfieldname": "outstanding_amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
@ -1338,6 +1314,30 @@
"set_only_once": 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,
"bold": 0,
@ -1350,7 +1350,7 @@
"in_list_view": 0,
"label": "Write Off Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"options": "currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
@ -1360,6 +1360,50 @@
"set_only_once": 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,
"bold": 0,
@ -1406,29 +1450,6 @@
"set_only_once": 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,
"bold": 0,
@ -1748,6 +1769,29 @@
"set_only_once": 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,
"bold": 0,
@ -1797,6 +1841,29 @@
"set_only_once": 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,
"bold": 0,

View File

@ -91,13 +91,16 @@ class PurchaseInvoice(BuyingController):
throw(_("Conversion rate cannot be 0 or 1"))
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":
frappe.throw(_("Credit To account must be a Balance Sheet account"))
if self.supplier and account.account_type != "Payable":
frappe.throw(_("Credit To account must be a Payable account"))
self.party_account_currency = account.account_currency
def check_for_stopped_status(self):
check_list = []
@ -213,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)
}
@ -248,7 +251,7 @@ class PurchaseInvoice(BuyingController):
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
gl_entries = []
# parent's gl entry
if self.base_grand_total:
gl_entries.append(
@ -257,26 +260,32 @@ class PurchaseInvoice(BuyingController):
"party_type": "Supplier",
"party": self.supplier,
"against": self.against_expense_account,
"credit": self.total_amount_to_pay,
"remarks": self.remarks,
"credit": self.base_grand_total,
"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_type": self.doctype,
})
}, self.party_account_currency)
)
# tax table gl entries
valuation_tax = {}
for tax in self.get("taxes"):
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(
self.get_gl_dict({
"account": tax.account_head,
"against": self.supplier,
"debit": tax.add_deduct_tax == "Add" and tax.base_tax_amount_after_discount_amount or 0,
"credit": tax.add_deduct_tax == "Deduct" and tax.base_tax_amount_after_discount_amount or 0,
"remarks": self.remarks,
dr_or_cr: tax.base_tax_amount_after_discount_amount,
dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
if account_currency==self.company_currency \
else tax.tax_amount_after_discount_amount,
"cost_center": tax.cost_center
})
}, account_currency)
)
# accumulate valuation tax
@ -292,14 +301,16 @@ class PurchaseInvoice(BuyingController):
stock_items = self.get_stock_items()
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = frappe.db.get_value("Account", item.expense_account, "account_currency")
gl_entries.append(
self.get_gl_dict({
"account": item.expense_account,
"against": self.supplier,
"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
})
}, account_currency)
)
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
# and the amount that is paid
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(
self.get_gl_dict({
"account": self.write_off_account,
"against": self.supplier,
"credit": flt(self.write_off_amount),
"remarks": self.remarks,
"credit": flt(self.base_write_off_amount),
"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
})
)

View File

@ -10,6 +10,7 @@ from frappe.utils import cint
import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records
from erpnext.controllers.accounts_controller import InvalidCurrency
test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"]
@ -218,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)
@ -276,6 +278,55 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEquals(expected_values[gle.account][1], gle.credit)
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):
pi = frappe.new_doc("Purchase Invoice")

View File

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

View File

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

View File

@ -175,6 +175,26 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
me.apply_pricing_rule();
})
},
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() {
this.calculate_total_advance();
@ -183,10 +203,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
write_off_outstanding_amount_automatically: function() {
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.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);
@ -199,10 +219,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
},
write_off_amount: function() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.write_off_outstanding_amount_automatically();
},
paid_amount: function() {
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
this.write_off_outstanding_amount_automatically();
},

View File

@ -1145,7 +1145,7 @@
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Discount",
"label": "Additional Discount",
"no_copy": 0,
"permlevel": 0,
"precision": "",
@ -1448,7 +1448,7 @@
"no_copy": 0,
"oldfieldname": "total_advance",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
@ -1472,7 +1472,7 @@
"no_copy": 1,
"oldfieldname": "outstanding_amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
@ -1663,7 +1663,7 @@
"no_copy": 1,
"oldfieldname": "paid_amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"options": "currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
@ -1673,6 +1673,29 @@
"set_only_once": 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,
"bold": 0,
@ -1710,7 +1733,7 @@
"in_list_view": 0,
"label": "Write Off Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"options": "currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
@ -1720,6 +1743,29 @@
"set_only_once": 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,
@ -2227,6 +2273,29 @@
"set_only_once": 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,
"bold": 0,

View File

@ -271,7 +271,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)
}
@ -282,13 +282,16 @@ class SalesInvoice(SellingController):
reconcile_against_document(lst)
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":
frappe.throw(_("Debit To account must be a Balance Sheet account"))
if self.customer and account.account_type != "Receivable":
frappe.throw(_("Debit To account must be a Receivable account"))
self.party_account_currency = account.account_currency
def validate_fixed_asset_account(self):
"""Validate Fixed Asset and whether Income Account Entered Exists"""
@ -434,15 +437,18 @@ class SalesInvoice(SellingController):
if cint(self.is_pos) == 1:
if flt(self.paid_amount) == 0:
if self.cash_bank_account:
frappe.db.set(self, 'paid_amount',
(flt(self.base_grand_total) - flt(self.write_off_amount)))
frappe.db.set(self, 'paid_amount',
flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount")))
else:
# show message that the amount is not paid
frappe.db.set(self,'paid_amount',0)
frappe.msgprint(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
else:
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):
for d in self.get('items'):
if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1:
@ -481,7 +487,7 @@ class SalesInvoice(SellingController):
from erpnext.accounts.general_ledger import merge_similar_entries
gl_entries = []
self.make_customer_gl_entry(gl_entries)
self.make_tax_gl_entries(gl_entries)
@ -498,7 +504,7 @@ class SalesInvoice(SellingController):
return gl_entries
def make_customer_gl_entry(self, gl_entries):
if self.base_grand_total:
if self.grand_total:
gl_entries.append(
self.get_gl_dict({
"account": self.debit_to,
@ -506,37 +512,42 @@ class SalesInvoice(SellingController):
"party": self.customer,
"against": self.against_income_account,
"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_type": self.doctype
})
}, self.party_account_currency)
)
def make_tax_gl_entries(self, gl_entries):
for tax in self.get("taxes"):
if flt(tax.base_tax_amount_after_discount_amount):
account_currency = frappe.db.get_value("Account", tax.account_head, "account_currency")
gl_entries.append(
self.get_gl_dict({
"account": tax.account_head,
"against": self.customer,
"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
})
}, account_currency)
)
def make_item_gl_entries(self, gl_entries):
# income account gl entries
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = frappe.db.get_value("Account", item.income_account, "account_currency")
gl_entries.append(
self.get_gl_dict({
"account": item.income_account,
"against": self.customer,
"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
})
}, account_currency)
)
# expense account gl entries
@ -546,6 +557,7 @@ class SalesInvoice(SellingController):
def make_pos_gl_entries(self, gl_entries):
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
gl_entries.append(
self.get_gl_dict({
@ -553,44 +565,50 @@ class SalesInvoice(SellingController):
"party_type": "Customer",
"party": self.customer,
"against": self.cash_bank_account,
"credit": self.paid_amount,
"remarks": self.remarks,
"credit": self.base_paid_amount,
"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_type": self.doctype,
})
}, self.party_account_currency)
)
gl_entries.append(
self.get_gl_dict({
"account": self.cash_bank_account,
"against": self.customer,
"debit": self.paid_amount,
"remarks": self.remarks,
})
"debit": self.base_paid_amount,
"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):
# write off entries, applicable if only pos
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(
self.get_gl_dict({
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"against": self.write_off_account,
"credit": self.write_off_amount,
"remarks": self.remarks,
"credit": self.base_write_off_amount,
"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_type": self.doctype,
})
"against_voucher_type": self.doctype
}, self.party_account_currency)
)
gl_entries.append(
self.get_gl_dict({
"account": self.write_off_account,
"against": self.customer,
"debit": self.write_off_amount,
"remarks": self.remarks,
"debit": self.base_write_off_amount,
"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
})
}, write_off_account_currency)
)
def get_list_context(context=None):

View File

@ -7,6 +7,8 @@ import unittest, copy
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.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):
def make(self):
@ -401,7 +403,7 @@ class TestSalesInvoice(unittest.TestCase):
jv.cancel()
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)
si = frappe.copy_doc(test_records[1])
si.insert()
@ -433,7 +435,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle)
def test_pos_gl_entry_with_aii(self):
def test_pos_gl_entry_with_perpetual_inventory(self):
set_perpetual_inventory()
self.make_pos_profile()
@ -442,8 +444,7 @@ class TestSalesInvoice(unittest.TestCase):
pos = copy.deepcopy(test_records[1])
pos["is_pos"] = 1
pos["update_stock"] = 1
# pos["posting_time"] = "12:05"
pos["cash_bank_account"] = "_Test Account Bank Account - _TC"
pos["cash_bank_account"] = "_Test Bank - _TC"
pos["paid_amount"] = 600.0
si = frappe.copy_doc(pos)
@ -474,7 +475,7 @@ class TestSalesInvoice(unittest.TestCase):
[stock_in_hand, 0.0, abs(sle.stock_value_difference)],
[pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.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)):
@ -494,7 +495,7 @@ class TestSalesInvoice(unittest.TestCase):
def make_pos_profile(self):
pos_profile = frappe.get_doc({
"cash_bank_account": "_Test Account Bank Account - _TC",
"cash_bank_account": "_Test Bank - _TC",
"company": "_Test Company",
"cost_center": "_Test Cost Center - _TC",
"currency": "INR",
@ -513,7 +514,7 @@ class TestSalesInvoice(unittest.TestCase):
if not frappe.db.exists("POS Profile", "_Test POS Profile"):
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()
frappe.delete_doc("Account", "_Test Warehouse No Account - _TC")
@ -567,7 +568,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle)
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()
si = frappe.get_doc(test_records[1])
@ -593,7 +594,7 @@ class TestSalesInvoice(unittest.TestCase):
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()
si = frappe.get_doc(test_records[1])
si.get("items")[0].item_code = "_Test Non Stock Item"
@ -660,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)
@ -841,7 +842,80 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(si.total_taxes_and_charges, 234.44)
self.assertEquals(si.base_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):
si = frappe.new_doc("Sales Invoice")
@ -856,14 +930,15 @@ def create_sales_invoice(**args):
si.is_pos = args.is_pos
si.is_return = args.is_return
si.return_against = args.return_against
si.currency="INR"
si.conversion_rate = 1
si.currency=args.currency or "INR"
si.conversion_rate = args.conversion_rate or 1
si.append("items", {
"item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": args.qty or 1,
"rate": args.rate or 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"serial_no": args.serial_no

View File

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

View File

@ -27,14 +27,25 @@ def process_gl_map(gl_map, merge_entries=True):
gl_map = merge_similar_entries(gl_map)
for entry in gl_map:
# toggle debit, credit if negative entry
# toggle debit, credit if negative entry
if flt(entry.debit) < 0:
entry.credit = flt(entry.credit) - flt(entry.debit)
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:
entry.debit = flt(entry.debit) - flt(entry.credit)
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
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)
if same_head:
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_in_account_currency = \
flt(same_head.credit_in_account_currency) + flt(entry.credit_in_account_currency)
else:
merged_gl_map.append(entry)

View File

@ -214,7 +214,8 @@ erpnext.AccountsChart = Class.extend({
'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'),
description: __("Optional. This setting will be used to filter in various transactions.") },
{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
from frappe import _, msgprint, scrub
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.contact.contact import get_contact_details
class InvalidCurrency(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
@frappe.whitelist()
def get_party_details(party=None, account=None, party_type="Customer", company=None,
posting_date=None, price_list=None, currency=None, doctype=None):
@ -141,7 +144,61 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
"due_date": get_due_date(posting_date, party_type, party, company)
}
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()
def get_party_account(company, party, party_type):
"""Returns the account for the given `party`.

View File

@ -30,19 +30,40 @@ class ReceivablePayableReport(object):
if args.get("party_type") == "Supplier":
columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"]
columns += [_("Invoiced Amount") + ":Currency:100", _("Paid Amount") + ":Currency:100",
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50",
"0-" + str(self.filters.range1) + ":Currency:100",
str(self.filters.range1) + "-" + str(self.filters.range2) + ":Currency:100",
str(self.filters.range2) + "-" + str(self.filters.range3) + ":Currency:100",
str(self.filters.range3) + _("-Above") + ":Currency:100"
]
for label in ("Invoiced Amount", "Paid Amount", "Outstanding Amount"):
columns.append({
"label": label,
"fieldtype": "Currency",
"options": "currency",
"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":
columns += [_("Territory") + ":Link/Territory:80"]
if args.get("party_type") == "Supplier":
columns += [_("Supplier Type") + ":Link/Supplier Type:80"]
columns += [_("Remarks") + "::200"]
columns += [
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Data",
"width": 100,
},
_("Remarks") + "::200"
]
return columns
@ -91,10 +112,11 @@ class ReceivablePayableReport(object):
# customer territory / supplier type
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":
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)
return data
@ -155,7 +177,7 @@ class ReceivablePayableReport(object):
def get_voucher_details(self, party_type):
voucher_details = frappe._dict()
if party_type == "Customer":
for si in frappe.db.sql("""select name, due_date
from `tabSales Invoice` where docstatus=1""", as_dict=1):
@ -171,17 +193,24 @@ class ReceivablePayableReport(object):
def get_gl_entries(self, party_type):
if not hasattr(self, "gl_entries"):
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`
where docstatus < 2 and party_type=%s {0} order by posting_date, party"""
.format(conditions), values, as_dict=True)
if self.filters.get(scrub(party_type)):
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
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
def prepare_conditions(self, party_type):
conditions = [""]
values = [party_type]
party_type_field = scrub(party_type)
if self.filters.company:
@ -190,7 +219,7 @@ class ReceivablePayableReport(object):
if self.filters.get(party_type_field):
conditions.append("party=%s")
values.append(self.filters.get(party_type_field))
values.append(self.filters.get(party_type_field))
return " and ".join(conditions), values

View File

@ -23,7 +23,8 @@ def execute(filters=None):
total_debit += flt(d[2])
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
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'
@ -38,7 +39,7 @@ def execute(filters=None):
data += [
get_balance_row(_("System Balance"), balance_as_per_system),
[""]*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),
[""]*len(columns),
get_balance_row(_("Expected balance as per bank"), bank_bal)
@ -49,13 +50,14 @@ def execute(filters=None):
def get_columns():
return [_("Posting Date") + ":Date:100", _("Journal Entry") + ":Link/Journal Entry:220",
_("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):
entries = frappe.db.sql("""select
jv.posting_date, jv.name, jvd.debit, jvd.credit,
jvd.against_account, jv.cheque_no, jv.cheque_date, jv.clearance_date
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.account_currency
from
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1
@ -68,6 +70,6 @@ def get_entries(filters):
def get_balance_row(label, amount):
if amount > 0:
return ["", '"' + label + '"', amount, 0, "", "", "", ""]
return ["", '"' + label + '"', amount, 0, "", "", "", "", ""]
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)
validate_filters(filters, account_details)
validate_party(filters)
filters = set_account_currency(filters)
columns = get_columns()
columns = get_columns(filters)
res = get_result(filters, account_details)
@ -43,36 +46,77 @@ def validate_party(filters):
frappe.throw(_("To filter based on Party, select Party Type first"))
elif not frappe.db.exists(party_type, party):
frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
def get_columns():
return [_("Posting Date") + ":Date:90", _("Account") + ":Link/Account:200",
_("Debit") + ":Float:100", _("Credit") + ":Float:100",
def set_account_currency(filters):
if not (filters.get("account") or filters.get("party")):
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",
_("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):
gl_entries = get_gl_entries(filters)
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
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" \
if filters.get("group_by_voucher") else "group by name"
gl_entries = frappe.db.sql("""select posting_date, account, party_type, party,
sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit,
voucher_type, voucher_no, cost_center, remarks, against, is_opening
sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit,
voucher_type, voucher_no, cost_center, remarks, against, is_opening {select_fields}
from `tabGL Entry`
where company=%(company)s {conditions}
{group_by_condition}
order by posting_date, account"""\
.format(conditions=get_conditions(filters), group_by_condition=group_by_condition),
filters, as_dict=1)
.format(select_fields=select_fields, conditions=get_conditions(filters),
group_by_condition=group_by_condition), filters, as_dict=1)
return gl_entries
@ -105,35 +149,51 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
data = []
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
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():
if acc_dict.entries:
# Opening for individual ledger, if grouped 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
# Totals and closing for individual ledger, if grouped 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,
"credit": acc_dict.total_credit},
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
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
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)"),
(opening + total_debit - total_credit)))
closing, closing_in_account_currency))
return data
@ -142,46 +202,83 @@ def initialize_gle_map(gl_entries):
for gle in gl_entries:
gle_map.setdefault(gle.account, frappe._dict({
"opening": 0,
"opening_in_account_currency": 0,
"entries": [],
"total_debit": 0,
"total_debit_in_account_currency": 0,
"total_credit": 0,
"closing": 0
"total_credit_in_account_currency": 0,
"closing": 0,
"closing_in_account_currency": 0
}))
return gle_map
def get_accountwise_gle(filters, gl_entries, gle_map):
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)
for gle in gl_entries:
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")) \
and (gle.posting_date < from_date or cstr(gle.is_opening) == "Yes"):
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"):
opening += amount
if filters.get("show_in_account_currency"):
opening_in_account_currency += amount_in_account_currency
elif gle.posting_date <= to_date:
gle_map[gle.account].entries.append(gle)
gle_map[gle.account].total_debit += flt(gle.debit, 3)
gle_map[gle.account].total_credit += flt(gle.credit, 3)
total_debit += flt(gle.debit, 3)
total_credit += flt(gle.credit, 3)
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)
total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
return opening, total_debit, total_credit, gle_map
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):
return {
def get_balance_row(label, balance, balance_in_account_currency=None):
balance_row = {
"account": "'" + label + "'",
"debit": balance if balance > 0 else 0,
"credit": -1*balance if balance < 0 else 0,
"credit": -1*balance if balance < 0 else 0
}
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):
def get_result_as_list(data, filters):
result = []
for d in data:
result.append([d.get("posting_date"), d.get("account"), d.get("debit"),
d.get("credit"), 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")])
row = [d.get("posting_date"), d.get("account"), d.get("debit"), d.get("credit")]
if filters.get("show_in_account_currency"):
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

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))
@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"):
account = frappe.form_dict.get("account")
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('"', '\\"')))
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("""
SELECT sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
SELECT {0}
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
return flt(bal)
@ -194,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
@ -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))
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)
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
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
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
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) {
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');
erpnext.buying.get_default_bom = function(frm) {

View File

@ -389,8 +389,31 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:!doc.__islocal",
"description": "Mention if non-standard receivable account applicable",
"fieldname": "party_account_currency",
"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",
"fieldtype": "Table",
"hidden": 0,

View File

@ -8,6 +8,7 @@ from frappe import msgprint, _
from frappe.model.naming import make_autoname
from erpnext.utilities.address_and_contact import load_address_and_contact
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_accounting_currency, validate_party_account
class Supplier(TransactionBase):
def get_feed(self):
@ -44,6 +45,9 @@ class Supplier(TransactionBase):
if frappe.defaults.get_global_default('supp_master_name') == 'Naming Series':
if not self.naming_series:
msgprint(_("Series is mandatory"), raise_exception=1)
validate_accounting_currency(self)
validate_party_account(self)
def get_contacts(self,nm):
if nm:

View File

@ -1,14 +1,22 @@
[
{
"company": "_Test Company",
"doctype": "Supplier",
"supplier_name": "_Test Supplier",
"supplier_type": "_Test Supplier Type"
},
{
"company": "_Test Company",
"doctype": "Supplier",
"supplier_name": "_Test Supplier 1",
"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")
class CustomerFrozen(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
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):
if self.get("_action") and self._action != "update_after_submit":
self.set_missing_values(for_validate=True)
@ -39,6 +50,7 @@ class AccountsController(TransactionBase):
self.validate_enabled_taxes_and_charges()
self.validate_party()
self.validate_currency()
def on_submit(self):
if self.meta.get_field("is_recurring"):
@ -95,8 +107,6 @@ class AccountsController(TransactionBase):
def set_price_list_currency(self, buying_or_selling):
if self.meta.get_field("currency"):
company_currency = get_company_currency(self.company)
# price list part
fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
else "buying_price_list"
@ -104,22 +114,22 @@ class AccountsController(TransactionBase):
self.price_list_currency = frappe.db.get_value("Price List",
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
elif not self.plc_conversion_rate:
self.plc_conversion_rate = get_exchange_rate(
self.price_list_currency, company_currency)
self.price_list_currency, self.company_currency)
# currency
if not self.currency:
self.currency = self.price_list_currency
self.conversion_rate = self.plc_conversion_rate
elif self.currency == company_currency:
elif self.currency == self.company_currency:
self.conversion_rate = 1.0
elif not self.conversion_rate:
self.conversion_rate = get_exchange_rate(self.currency,
company_currency)
self.company_currency)
def set_missing_item_details(self):
"""set missing item values"""
@ -156,7 +166,7 @@ class AccountsController(TransactionBase):
item.set("discount_percentage", ret.get("discount_percentage"))
if ret.get("pricing_rule_for") == "Price":
item.set("pricing_list_rate", ret.get("pricing_list_rate"))
def set_taxes(self):
if not self.meta.get_field("taxes"):
@ -187,7 +197,7 @@ class AccountsController(TransactionBase):
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))
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"""
gl_dict = frappe._dict({
'company': self.company,
@ -198,11 +208,51 @@ class AccountsController(TransactionBase):
'fiscal_year': self.fiscal_year,
'debit': 0,
'credit': 0,
'debit_in_account_currency': 0,
'credit_in_account_currency': 0,
'is_opening': self.get("is_opening") or "No",
'party_type': None,
'party': None
})
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
def clear_unallocated_advances(self, childtype, parentfield):
@ -217,13 +267,13 @@ class AccountsController(TransactionBase):
# conver sales_order to "Sales Order"
reference_type = against_order_field.replace("_", " ").title()
condition = ""
if order_list:
in_placeholder = ', '.join(['%s'] * len(order_list))
condition = "or (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
.format(reference_type, in_placeholder)
res = frappe.db.sql("""
select
t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no,
@ -321,9 +371,9 @@ class AccountsController(TransactionBase):
def set_total_advance_paid(self):
if self.doctype == "Sales Order":
dr_or_cr = "credit"
dr_or_cr = "credit_in_account_currency"
else:
dr_or_cr = "debit"
dr_or_cr = "debit_in_account_currency"
advance_paid = frappe.db.sql("""
select
@ -331,10 +381,9 @@ class AccountsController(TransactionBase):
from
`tabJournal Entry Account`
where
reference_type = %s and
reference_name = %s and
docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr),
(self.doctype, self.name))
reference_type = %s and reference_name = %s
and docstatus = 1 and is_advance = "Yes"
""".format(dr_or_cr=dr_or_cr), (self.doctype, self.name))
if 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():
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
if self.meta.get_field("customer"):
party_type = 'Customer'
@ -364,10 +420,20 @@ class AccountsController(TransactionBase):
elif self.meta.get_field("supplier"):
party_type = 'Supplier'
if party_type:
party = self.get(party_type.lower())
if frappe.db.get_value(party_type, party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen)
party = self.get(party_type.lower()) if party_type else None
return party_type, party
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()
def get_tax_rate(account_head):

View File

@ -46,22 +46,22 @@ class StockController(AccountsController):
# from warehouse account
self.check_expense_account(detail)
gl_list.append(self.get_gl_dict({
"account": warehouse_account[sle.warehouse],
"account": warehouse_account[sle.warehouse]["name"],
"against": detail.expense_account,
"cost_center": detail.cost_center,
"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({
"account": detail.expense_account,
"against": warehouse_account[sle.warehouse],
"against": warehouse_account[sle.warehouse]["name"],
"cost_center": detail.cost_center,
"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:
warehouse_with_no_account.append(sle.warehouse)
@ -69,7 +69,7 @@ class StockController(AccountsController):
if warehouse_with_no_account:
msgprint(_("No accounting entries for the following warehouses") + ": \n" +
"\n".join(warehouse_with_no_account))
return process_gl_map(gl_list)
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
@ -336,6 +336,9 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
return gl_entries
def get_warehouse_account():
warehouse_account = dict(frappe.db.sql("""select warehouse, name from tabAccount
where account_type = 'Warehouse' and ifnull(warehouse, '') != ''"""))
warehouse_account = frappe._dict()
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

View File

@ -394,17 +394,21 @@ class calculate_taxes_and_totals(object):
# NOTE:
# write_off_amount is only for 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 not self.doc.is_return:
self.doc.round_floats_in(self.doc, ["base_grand_total", "total_advance", "write_off_amount", "paid_amount"])
total_amount_to_pay = self.doc.base_grand_total - self.doc.write_off_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount,
self.doc.precision("outstanding_amount"))
self.doc.round_floats_in(self.doc, ["paid_amount"])
outstanding_amount = flt(total_amount_to_pay - self.doc.paid_amount, self.doc.precision("outstanding_amount"))
elif self.doc.doctype == "Purchase Invoice":
outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
if self.doc.party_account_currency == self.doc.currency:
self.doc.outstanding_amount = outstanding_amount
else:
self.doc.round_floats_in(self.doc, ["total_advance", "write_off_amount"])
self.doc.total_amount_to_pay = flt(self.doc.base_grand_total - self.doc.write_off_amount,
self.doc.precision("total_amount_to_pay"))
if not self.doc.is_return:
self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
self.doc.precision("outstanding_amount"))
self.doc.outstanding_amount = flt(outstanding_amount * self.doc.conversion_rate,
self.doc.precision("outstanding_amount"))

View File

@ -6,6 +6,7 @@ import frappe, json
from frappe.utils import cstr, cint
from frappe import msgprint, _
from frappe.model.mapper import get_mapped_doc
from erpnext.setup.utils import get_exchange_rate
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 set_missing_values(source, 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("calculate_taxes_and_totals")

View File

@ -205,4 +205,5 @@ erpnext.patches.v6_0.default_activity_rate
execute:frappe.db.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
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_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.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",
"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",
"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",
"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
+ " = [?] " + company_currency)
@ -440,7 +443,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
// toggle fields
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_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.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.cscript.calculate_taxes_and_totals();
var paid_amount = flt((flt(values.paid_amount) - flt(values.change)) / me.frm.doc.conversion_rate, precision("paid_amount"));
var paid_amount = flt((flt(values.paid_amount) - flt(values.change)), precision("paid_amount"));
me.frm.set_value("paid_amount", paid_amount);
// 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.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);
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) {
@ -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) {
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 {
filters: {
'account_type': 'Receivable',
'company': d.company,
"is_group": 0
}
filters: filters
}
}

View File

@ -273,7 +273,7 @@
"ignore_user_permissions": 1,
"in_filter": 0,
"in_list_view": 0,
"label": "Currency",
"label": "Default Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
@ -463,8 +463,31 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:!doc.__islocal",
"description": "Mention if non-standard receivable account applicable",
"fieldname": "party_account_currency",
"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",
"fieldtype": "Table",
"hidden": 0,
@ -567,7 +590,7 @@
"no_copy": 0,
"oldfieldname": "credit_limit",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"options": "",
"permlevel": 1,
"print_hide": 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.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
class Customer(TransactionBase):
@ -27,12 +28,14 @@ class Customer(TransactionBase):
else:
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:
frappe.throw(_("Series is mandatory"), frappe.MandatoryError)
def validate(self):
self.validate_values()
self.validate_mandatory()
validate_accounting_currency(self)
validate_party_account(self)
def update_lead_status(self):
if self.lead_name:

View File

@ -8,6 +8,8 @@ import unittest
from frappe.test_runner import make_test_records
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"]
@ -77,4 +79,14 @@ class TestCustomer(unittest.TestCase):
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_name": "_Test Customer",
"customer_type": "Individual",
@ -8,7 +7,6 @@
"territory": "_Test Territory"
},
{
"company": "_Test Company",
"customer_group": "_Test Customer Group",
"customer_name": "_Test Customer 1",
"customer_type": "Individual",
@ -16,7 +14,6 @@
"territory": "_Test Territory"
},
{
"company": "_Test Company",
"customer_group": "_Test Customer Group",
"customer_name": "_Test Customer 2",
"customer_type": "Individual",
@ -24,11 +21,22 @@
"territory": "_Test Territory"
},
{
"company": "_Test Company",
"customer_group": "_Test Customer Group",
"customer_name": "_Test Customer 3",
"customer_type": "Individual",
"doctype": "Customer",
"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,
"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,
"bold": 0,
@ -210,7 +185,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2013-12-31 19:00:14",
"modified": "2015-08-13 16:30:24.146848",
"modified_by": "Administrator",
"module": "Selling",
"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() {
if(this.frm.fields_dict.commission_rate) {
if(this.frm.doc.commission_rate > 100) {

View File

@ -34,13 +34,8 @@ class Company(Document):
if not self.abbr.strip():
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_currency()
def validate_default_accounts(self):
for field in ["default_bank_account", "default_cash_account", "default_receivable_account", "default_payable_account",
@ -51,6 +46,13 @@ class Company(Document):
if for_company != self.name:
frappe.throw(_("Account {0} does not belong to company: {1}")
.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):
if not frappe.db.sql("""select name from tabAccount

View File

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

View File

@ -10,17 +10,6 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
this._super();
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)
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);

View File

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