Multi currency: test case and fixes

This commit is contained in:
Nabin Hait 2015-08-28 19:24:22 +05:30
parent 78be566428
commit 6e439a5e53
28 changed files with 495 additions and 199 deletions

View File

@ -11,6 +11,8 @@
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "properties",
"fieldtype": "Section Break",
"hidden": 0,
@ -31,6 +33,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
@ -50,6 +54,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "account_name",
"fieldtype": "Data",
"hidden": 0,
@ -71,6 +77,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "0",
"fieldname": "is_group",
"fieldtype": "Check",
@ -92,6 +100,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -114,6 +124,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "root_type",
"fieldtype": "Select",
"hidden": 0,
@ -134,6 +146,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "report_type",
"fieldtype": "Select",
"hidden": 0,
@ -154,7 +168,10 @@
},
{
"allow_on_submit": 0,
"fieldname": "currency",
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.is_group==0",
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
@ -175,6 +192,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@ -194,6 +213,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "parent_account",
"fieldtype": "Link",
"hidden": 0,
@ -216,6 +237,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "",
"fieldname": "account_type",
"fieldtype": "Select",
@ -239,6 +262,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "Rate at which this tax is applied",
"fieldname": "tax_rate",
"fieldtype": "Float",
@ -261,6 +286,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "If the account is frozen, entries are allowed to restricted users.",
"fieldname": "freeze_account",
"fieldtype": "Select",
@ -284,6 +311,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "warehouse",
"fieldtype": "Link",
"hidden": 0,
@ -304,6 +333,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "balance_must_be",
"fieldtype": "Select",
"hidden": 0,
@ -324,6 +355,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "lft",
"fieldtype": "Int",
"hidden": 1,
@ -343,6 +376,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "rgt",
"fieldtype": "Int",
"hidden": 1,
@ -362,6 +397,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "old_parent",
"fieldtype": "Data",
"hidden": 1,
@ -389,7 +426,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-08-13 16:43:10.645538",
"modified": "2015-08-28 17:17:20.899845",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",

View File

@ -89,10 +89,10 @@ class Account(Document):
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.currency:
self.currency = frappe.db.get_value("Company", self.company, "default_currency")
if not self.account_currency:
self.account_currency = frappe.db.get_value("Company", self.company, "default_currency")
elif self.currency != frappe.db.get_value("Account", self.name, "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"))

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

@ -10,6 +10,8 @@
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
@ -31,6 +33,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "transaction_date",
"fieldtype": "Date",
"hidden": 0,
@ -52,6 +56,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "account",
"fieldtype": "Link",
"hidden": 0,
@ -74,6 +80,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party_type",
"fieldtype": "Link",
"hidden": 0,
@ -94,6 +102,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party",
"fieldtype": "Dynamic Link",
"hidden": 0,
@ -114,6 +124,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
@ -136,6 +148,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "debit",
"fieldtype": "Currency",
"hidden": 0,
@ -158,6 +172,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "credit",
"fieldtype": "Currency",
"hidden": 0,
@ -180,7 +196,9 @@
},
{
"allow_on_submit": 0,
"fieldname": "currency",
"bold": 0,
"collapsible": 0,
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
@ -201,6 +219,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "debit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
@ -222,6 +242,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "credit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
@ -243,6 +265,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against",
"fieldtype": "Text",
"hidden": 0,
@ -264,6 +288,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against_voucher_type",
"fieldtype": "Link",
"hidden": 0,
@ -286,6 +312,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against_voucher",
"fieldtype": "Dynamic Link",
"hidden": 0,
@ -308,6 +336,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "voucher_type",
"fieldtype": "Link",
"hidden": 0,
@ -330,6 +360,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"hidden": 0,
@ -352,6 +384,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "remarks",
"fieldtype": "Text",
"hidden": 0,
@ -373,6 +407,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "is_opening",
"fieldtype": "Select",
"hidden": 0,
@ -395,6 +431,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "is_advance",
"fieldtype": "Select",
"hidden": 0,
@ -417,6 +455,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
@ -439,6 +479,8 @@
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -469,7 +511,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-08-18 14:25:44.430671",
"modified": "2015-08-28 17:14:52.661217",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",

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):
@ -102,27 +104,31 @@ class GLEntry(Document):
def validate_currency(self):
company_currency = frappe.db.get_value("Company", self.company, "default_currency")
account_currency = frappe.db.get_value("Account", self.account, "currency") or company_currency
account_currency = frappe.db.get_value("Account", self.account, "account_currency") or company_currency
if not self.currency:
self.currency = company_currency
if account_currency != self.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)))
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
if self.party_type and self.party:
existing_gle = frappe.db.get_value("GL Entry", {"party_type": self.party_type,
"party": self.party, "company": self.company}, ["name", "currency"], as_dict=1)
"party": self.party, "company": self.company}, ["name", "account_currency"], as_dict=1)
if not existing_gle:
party_currency = frappe.db.get_value(self.party_type, self.party, "default_currency") or company_currency
party_currency = frappe.db.get_value(self.party_type, self.party, "default_currency")\
or company_currency
if party_currency != account_currency:
frappe.throw(_("Invalid Account {0}. Account Currency must be {1}, same as {2}: {3}")
.format(self.account, party_currency, self.party_type, self.party))
.format(self.account, party_currency, self.party_type, self.party),
InvalidAccountCurrency)
else:
currency_in_existing_entries = existing_gle.currency or company_currency
if currency_in_existing_entries != self.currency:
currency_in_existing_entries = existing_gle.account_currency or company_currency
if currency_in_existing_entries != self.account_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(self.party_type, self.party, currency_in_existing_entries))
.format(self.party_type, self.party, currency_in_existing_entries),
InvalidAccountCurrency)
def validate_balance_type(account, adv_adj=False):
if not adv_adj and account:

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

@ -35,7 +35,7 @@ erpnext.journal_entry.toggle_fields_based_on_currency = function(frm) {
var company_currency = erpnext.get_currency(frm.doc.company);
var grid = frm.get_field("accounts").grid;
grid.set_column_disp(fields, grid.currency!=company_currency);
grid.set_column_disp(fields, grid.account_currency!=company_currency);
}
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
@ -324,7 +324,7 @@ frappe.ui.form.on("Journal Entry Account", {
$.extend(d, r.message[0]);
refresh_field('balance', d.name, 'accounts');
refresh_field('party_type', d.name, 'accounts');
refresh_field('currency', d.name, 'accounts');
refresh_field('account_currency', d.name, 'accounts');
if(r.message[1] && (!frm.doc.exchange_rate || frm.doc.exchange_rate == 1.0)) {
frm.set_value("exchange_rate", r.message[1])
@ -339,7 +339,7 @@ frappe.ui.form.on("Journal Entry Account", {
var company_currency = erpnext.get_currency(frm.doc.company);
var row = locals[dt][dn];
var exchange_rate = (row.currency==company_currency) ? 1 : frm.doc.exchange_rate;
var exchange_rate = (row.account_currency==company_currency) ? 1 : frm.doc.exchange_rate;
frappe.model.set_value(dt, dn, "debit",
flt(flt(row.debit_in_account_currency)*exchange_rate), precision("debit", row));
@ -349,7 +349,7 @@ frappe.ui.form.on("Journal Entry Account", {
var company_currency = erpnext.get_currency(frm.doc.company);
var row = locals[dt][dn];
var exchange_rate = (row.currency==company_currency) ? 1 : frm.doc.exchange_rate;
var exchange_rate = (row.account_currency==company_currency) ? 1 : frm.doc.exchange_rate;
frappe.model.set_value(dt, dn, "credit",
flt(flt(row.credit_in_account_currency)*exchange_rate), precision("credit", row));

View File

@ -3,7 +3,7 @@
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, get_exchange_rate
from erpnext.controllers.accounts_controller import AccountsController
@ -248,13 +248,18 @@ class JournalEntry(AccountsController):
self.difference = flt(self.total_debit, self.precision("total_debit")) - \
flt(self.total_credit, self.precision("total_credit"))
print self.difference
if self.difference:
frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}")
.format(self.difference))
def validate_multi_currency(self):
alternate_currency = [d.currency for d in self.get("accounts") if d.currency!=self.company_currency]
alternate_currency = []
for d in self.get("accounts"):
d.account_currency = frappe.db.get_value("Account", d.account, "account_currency") or self.company_currency
if d.account_currency!=self.company_currency:
alternate_currency.append(d.account_currency)
if alternate_currency:
if not self.exchange_rate:
@ -266,10 +271,7 @@ class JournalEntry(AccountsController):
self.exchange_rate = 1.0
for d in self.get("accounts"):
if not d.currency:
d.currency = frappe.db.get_value("Account", d.account, "currency") or self.company_currency
exchange_rate = self.exchange_rate if d.currency != self.company_currency else 1
exchange_rate = self.exchange_rate if d.account_currency != self.company_currency else 1
d.debit = flt(flt(d.debit_in_account_currency)*exchange_rate, d.precision("debit"))
d.credit = flt(flt(d.credit_in_account_currency)*exchange_rate, d.precision("credit"))
@ -340,7 +342,7 @@ class JournalEntry(AccountsController):
"against": d.against_account,
"debit": flt(d.debit, d.precision("debit")),
"credit": flt(d.credit, d.precision("credit")),
"currency": d.currency,
"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,
@ -488,12 +490,12 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
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].credit_in_account_currency = si.outstanding_amount
jv.get("accounts")[0].reference_type = si.doctype
jv.get("accounts")[0].reference_name = si.name
# debit bank
jv.get("accounts")[1].debit = si.outstanding_amount
jv.get("accounts")[1].debit_in_account_currency = si.outstanding_amount
return jv.as_dict()
@ -510,12 +512,12 @@ def get_payment_entry_from_purchase_invoice(purchase_invoice):
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].debit_in_account_currency = pi.outstanding_amount
jv.get("accounts")[0].reference_type = pi.doctype
jv.get("accounts")[0].reference_name = pi.name
# credit bank
jv.get("accounts")[1].credit = pi.outstanding_amount
jv.get("accounts")[1].credit_in_account_currency = pi.outstanding_amount
return jv.as_dict()
@ -524,6 +526,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:
@ -531,9 +534,15 @@ 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_currency = frappe.db.get_value("Account", party_account, "account_currency")
company_currency = get_company_currency(so.company)
if party_account_currency == 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
@ -541,13 +550,13 @@ def get_payment_entry_from_sales_order(sales_order):
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].credit_in_account_currency = amount
jv.get("accounts")[0].reference_type = so.doctype
jv.get("accounts")[0].reference_name = so.name
jv.get("accounts")[0].is_advance = "Yes"
# debit bank
jv.get("accounts")[1].debit = amount
jv.get("accounts")[1].debit_in_account_currency = amount
return jv.as_dict()
@ -563,9 +572,15 @@ 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_currency = frappe.db.get_value("Account", party_account, "account_currency")
company_currency = get_company_currency(po.company)
if party_account_currency == 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
@ -573,13 +588,13 @@ def get_payment_entry_from_purchase_order(purchase_order):
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].debit_in_account_currency = amount
jv.get("accounts")[0].reference_type = po.doctype
jv.get("accounts")[0].reference_name = po.name
jv.get("accounts")[0].is_advance = "Yes"
# debit bank
jv.get("accounts")[1].credit = amount
jv.get("accounts")[1].credit_in_account_currency = amount
return jv.as_dict()
@ -669,7 +684,7 @@ def get_account_balance_and_party_type(account, date, company):
frappe.msgprint(_("No Permission"), raise_exception=1)
company_currency = get_company_currency(company)
account_details = frappe.db.get_value("Account", account, ["account_type", "currency"], as_dict=1)
account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
if account_details.account_type == "Receivable":
party_type = "Customer"
@ -679,12 +694,12 @@ def get_account_balance_and_party_type(account, date, company):
party_type = ""
exchange_rate = None
if account_details.currency != company_currency:
exchange_rate = get_exchange_rate(account_details.currency, company_currency)
if account_details.account_currency != company_currency:
exchange_rate = get_exchange_rate(account_details.account_currency, company_currency)
grid_values = {
"balance": get_balance_on(account, date),
"party_type": party_type,
"currency": account_details.currency or company_currency,
"account_currency": account_details.account_currency or company_currency,
}
return grid_values, 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,79 @@ 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.exchange_rate = exchange_rate
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,
}, {
"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,
}
])
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

@ -38,13 +38,13 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "currency",
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Currency",
"label": "Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
@ -520,7 +520,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2015-08-27 16:09:43.872157",
"modified": "2015-08-28 17:15:25.180681",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@ -33,7 +33,8 @@ 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')
@ -41,7 +42,8 @@ class PaymentTool(Document):
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)

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

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

@ -269,7 +269,7 @@ class PurchaseInvoice(BuyingController):
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, "currency")
account_currency = frappe.db.get_value("Account", tax.account_head, "account_currency")
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
@ -298,7 +298,7 @@ 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, "currency")
account_currency = frappe.db.get_value("Account", item.expense_account, "account_currency")
gl_entries.append(
self.get_gl_dict({
"account": item.expense_account,
@ -360,7 +360,7 @@ 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, "currency")
write_off_account_currency = frappe.db.get_value("Account", self.write_off_account, "account_currency")
gl_entries.append(
self.get_gl_dict({

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"]
@ -276,6 +277,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

@ -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": "",
@ -2951,7 +2951,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-08-27 16:12:12.077662",
"modified": "2015-08-27 16:46:11.526089",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@ -423,22 +423,18 @@ class SalesInvoice(SellingController):
if cint(self.is_pos) == 1:
if flt(self.paid_amount) == 0:
if self.cash_bank_account:
paid_amount = flt(flt(self.grand_total) - flt(self.write_off_amount),
self.precision("paid_amount"))
base_paid_amount = flt(paid_amount*self.conversion_rate, self.precision("base_paid_amount"))
frappe.db.set(self, 'paid_amount', paid_amount)
frappe.db.set(self, 'base_paid_amount', base_paid_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.db.set(self,'base_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',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:
@ -512,7 +508,7 @@ class SalesInvoice(SellingController):
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, "currency")
account_currency = frappe.db.get_value("Account", tax.account_head, "account_currency")
gl_entries.append(
self.get_gl_dict({
"account": tax.account_head,
@ -528,7 +524,7 @@ class SalesInvoice(SellingController):
# 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, "currency")
account_currency = frappe.db.get_value("Account", item.income_account, "account_currency")
gl_entries.append(
self.get_gl_dict({
"account": item.income_account,
@ -547,7 +543,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, "currency")
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({
@ -575,7 +571,7 @@ class SalesInvoice(SellingController):
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, "currency")
write_off_account_currency = frappe.db.get_value("Account", self.write_off_account, "account_currency")
gl_entries.append(
self.get_gl_dict({

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

@ -212,7 +212,7 @@ erpnext.AccountsChart = Class.extend({
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:'currency', label:__('Currency'), options:"Currency"}
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency"}
]
})

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",
"default_currency": "USD",
"accounts": [{
"company": "_Test Company",
"account": "_Test Payable USD - _TC"
}]
}
]
]

View File

@ -14,6 +14,7 @@ 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):
@ -106,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"
@ -115,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"""
@ -218,7 +217,7 @@ class AccountsController(TransactionBase):
gl_dict.update(args)
if not account_currency:
account_currency = frappe.db.get_value("Account", gl_dict.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)
@ -226,17 +225,23 @@ class AccountsController(TransactionBase):
return gl_dict
def validate_account_currency(self, account, account_currency=None):
valid_currency = list(set([self.currency, self.company_currency]))
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 account_currency!=self.company_currency:
frappe.throw(_("Account: {0} with currency: {1} can not be selected")
.format(gl_dict.account, account_currency))
if not (self.get("conversion_rate") or self.get("exchange_rate")) \
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["currency"] = self.company_currency if account_currency==self.company_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
@ -366,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
@ -376,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"))
@ -421,23 +425,23 @@ class AccountsController(TransactionBase):
return party_type, party
def validate_currency(self):
if self.get("currency") and self.currency != self.company_currency:
if self.get("currency"):
party_type, party = self.get_party()
existing_gle = frappe.db.get_value("GL Entry", {"party_type": party_type,
"party": party, "company": self.company}, ["name", "currency"], as_dict=1)
currency_in_existing_entries = existing_gle.currency or self.company_currency
if existing_gle:
if currency_in_existing_entries != self.company_currency \
and currency_in_existing_entries != self.currency:
frappe.throw(_("Currency must be {0} for {1} {2}")
.format(currency_in_existing_entries, party_type, party))
else:
party_currency = frappe.db.get_value(party_type, party, "default_currency")
if party_currency != self.company_currency and self.currency != party_currency:
frappe.throw(_("Currency must be same as {0} currency {1}")
.format(party_type, party_currency))
if party_type and party:
existing_gle = frappe.db.get_value("GL Entry", {"party_type": party_type,
"party": party, "company": self.company}, ["name", "account_currency"], as_dict=1)
if existing_gle:
currency_in_existing_entries = existing_gle.account_currency or self.company_currency
if currency_in_existing_entries != self.company_currency \
and currency_in_existing_entries != self.currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(party_type, party, currency_in_existing_entries), InvalidCurrency)
else:
party_currency = frappe.db.get_value(party_type, party, "default_currency") \
or self.company_currency
if party_currency != self.company_currency and self.currency != party_currency:
frappe.throw(_("Currency must be same as {0} currency {1}")
.format(party_type, party_currency), InvalidCurrency)
@frappe.whitelist()
def get_tax_rate(account_head):

View File

@ -53,7 +53,7 @@ class StockController(AccountsController):
"cost_center": detail.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": flt(sle.stock_value_difference, 2),
}, warehouse_account[sle.warehouse]["currency"]))
}, warehouse_account[sle.warehouse]["account_currency"]))
# to target warehouse / expense account
gl_list.append(self.get_gl_dict({
@ -338,7 +338,7 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
def get_warehouse_account():
warehouse_account = frappe._dict()
for d in frappe.db.sql("""select warehouse, name, currency from tabAccount
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

@ -77,7 +77,7 @@ def execute():
party_account = party_gle.account or company.default_receivable_account
party.append("party_accounts", {
party.append("accounts", {
"company": company.name,
"account": party_account
})

View File

@ -567,7 +567,7 @@
"no_copy": 0,
"oldfieldname": "credit_limit",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"options": "",
"permlevel": 1,
"print_hide": 0,
"read_only": 0,
@ -796,7 +796,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-08-27 16:21:57.520411",
"modified": "2015-08-27 17:00:50.604869",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",

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",
"default_currency": "USD",
"accounts": [{
"company": "_Test Company",
"account": "_Test Receivable USD - _TC"
}]
}
]

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

@ -316,10 +316,10 @@ class PurchaseReceipt(BuyingController):
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": stock_value_diff
}, warehouse_account[d.warehouse]["currency"]))
}, warehouse_account[d.warehouse]["account_currency"]))
# stock received but not billed
stock_rbnb_currency = frappe.db.get_value("Account", stock_rbnb, "currency")
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]["name"],
@ -350,14 +350,14 @@ class PurchaseReceipt(BuyingController):
"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]["currency"]))
}, 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),
self.precision("base_net_amount", d))
distributed_amount = flt(flt(d.base_net_amount, self.precision("base_net_amount", d))) + \
flt(d.landed_cost_voucher_amount) + flt(d.rm_supp_cost)
flt(d.landed_cost_voucher_amount) + flt(d.rm_supp_cost) + flt(d.item_tax_amount)
divisional_loss = flt(distributed_amount - sle_valuation_amount, self.precision("base_net_amount", d))
if divisional_loss: