Merge pull request #4067 from anandpdoshi/remove-party-account-currency

Remove party_account_currency from Customer and Supplier, and use currency derived from get_party_account
This commit is contained in:
Anand Doshi 2015-09-30 16:55:24 +05:30
commit c320fe541d
26 changed files with 1782 additions and 1838 deletions

View File

@ -50,20 +50,20 @@ class Account(Document):
def set_root_and_report_type(self):
if self.parent_account:
par = frappe.db.get_value("Account", self.parent_account, ["report_type", "root_type"], as_dict=1)
if par.report_type:
self.report_type = par.report_type
if par.root_type:
self.root_type = par.root_type
if self.is_group:
db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
if db_value:
if self.report_type != db_value.report_type:
frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
(self.report_type, self.lft, self.rgt))
if self.root_type != db_value.root_type:
frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
(self.root_type, self.lft, self.rgt))
def validate_root_details(self):
@ -89,11 +89,11 @@ 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"))
@ -207,3 +207,14 @@ def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
and %s like %s order by name limit %s, %s""" %
("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)
def get_account_currency(account):
"""Helper function to get account currency"""
def generator():
account_currency, company = frappe.db.get_value("Account", account, ["account_currency", "company"])
if not account_currency:
account_currency = frappe.db.get_value("Company", company, "default_currency")
return account_currency
return frappe.local_cache("account_currency", account, generator)

View File

@ -3,15 +3,13 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe.model.document import Document
class CustomerFrozen(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
from erpnext.accounts.party import validate_party_gle_currency, get_party_account_currency
from erpnext.accounts.utils import get_account_currency
from erpnext.setup.doctype.company.company import get_company_currency
from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
class GLEntry(Document):
def validate(self):
@ -101,25 +99,26 @@ 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
company_currency = get_company_currency(self.company)
account_currency = get_account_currency(self.account)
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
party_account_currency = get_party_account_currency(self.party_type, self.party, self.company)
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)
.format(self.party_type, self.party, party_account_currency), InvalidAccountCurrency)
validate_party_gle_currency(self.party_type, self.party, self.company)
def validate_balance_type(account, adv_adj=False):
if not adv_adj and account:
@ -159,7 +158,7 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
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":

View File

@ -6,7 +6,8 @@ import frappe
from frappe.utils import cstr, flt, fmt_money, formatdate
from frappe import msgprint, _, scrub
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on
from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.accounts.party import get_party_account_currency
from erpnext.setup.utils import get_company_currency
@ -146,7 +147,7 @@ class JournalEntry(AccountsController):
self.reference_totals = {}
self.reference_types = {}
self.reference_parties = {}
self.reference_accounts = {}
for d in self.get("accounts"):
if not d.reference_type:
@ -169,8 +170,7 @@ 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]
self.reference_accounts[d.reference_name] = d.account
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
[scrub(dt) for dt in field_dict.get(d.reference_type)])
@ -196,7 +196,7 @@ 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)
account = self.reference_accounts[reference_name]
if reference_type in ("Sales Order", "Purchase Order"):
order = frappe.db.get_value(reference_type, reference_name,
@ -212,8 +212,8 @@ class JournalEntry(AccountsController):
if cstr(order.status) == "Stopped":
frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name))
party_account_currency = frappe.db.get_value(party_type, party, "party_account_currency")
if party_account_currency == self.company_currency:
account_currency = get_account_currency(account)
if account_currency == self.company_currency:
voucher_total = order.base_grand_total
else:
voucher_total = order.grand_total
@ -609,8 +609,8 @@ 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")
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
party_account = get_party_account("Customer", so.customer, so.company)
party_account_currency = get_account_currency(party_account)
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company)
@ -660,8 +660,8 @@ 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")
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
party_account = get_party_account("Supplier", po.supplier, po.company)
party_account_currency = get_account_currency(party_account)
exchange_rate = get_exchange_rate(party_account, party_account_currency, po.company)
@ -779,7 +779,7 @@ def get_party_account_and_balance(company, party_type, party):
frappe.msgprint(_("No Permission"), raise_exception=1)
from erpnext.accounts.party import get_party_account
account = get_party_account(company, party, party_type)
account = get_party_account(party_type, party, company)
account_balance = get_balance_on(account=account)
party_balance = get_balance_on(party_type=party_type, party=party)

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import unittest, frappe
from frappe.utils import flt
from erpnext.accounts.utils import get_actual_expense, BudgetError, get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency
class TestJournalEntry(unittest.TestCase):
@ -166,15 +167,15 @@ class TestJournalEntry(unittest.TestCase):
existing_expense = self.get_actual_expense(posting_date)
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_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,
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)
@ -197,12 +198,10 @@ class TestJournalEntry(unittest.TestCase):
"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()
@ -212,6 +211,40 @@ class TestJournalEntry(unittest.TestCase):
self.assertFalse(gle)
def test_disallow_change_in_account_currency_for_a_party(self):
# create jv in USD
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Receivable USD - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
jv.submit()
# create jv in USD, but account currency in INR
jv = make_journal_entry("_Test Bank - _TC",
"_Test Receivable - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
self.assertRaises(InvalidAccountCurrency, jv.submit)
# back in USD
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Receivable USD - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
jv.submit()
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"
@ -231,7 +264,7 @@ def make_journal_entry(account1, account2, amount, cost_center=None, exchange_ra
"cost_center": cost_center,
"credit_in_account_currency": amount if amount > 0 else 0,
"debit_in_account_currency": abs(amount) if amount < 0 else 0,
exchange_rate: exchange_rate
"exchange_rate": exchange_rate
}
])
if save or submit:

View File

@ -7,6 +7,7 @@ from frappe import _, scrub
from frappe.utils import flt
from frappe.model.document import Document
import json
from erpnext.accounts.utils import get_account_currency
class PaymentTool(Document):
def make_journal_entry(self):
@ -59,7 +60,7 @@ def get_outstanding_vouchers(args):
args = json.loads(args)
party_account_currency = frappe.db.get_value("Account", args.get("party_account"), "account_currency")
party_account_currency = get_account_currency(args.get("party_account"))
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":
@ -112,7 +113,7 @@ def get_orders_to_be_billed(party_type, party, party_account_currency, company_c
@frappe.whitelist()
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")
party_account_currency = get_account_currency(party_account)
company_currency = frappe.db.get_value("Company", company, "default_currency")
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"

View File

@ -10,6 +10,7 @@ import frappe.defaults
from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -66,7 +67,7 @@ class PurchaseInvoice(BuyingController):
def set_missing_values(self, for_validate=False):
if not self.credit_to:
self.credit_to = get_party_account(self.company, self.supplier, "Supplier")
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
if not self.due_date:
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
@ -91,7 +92,7 @@ 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 = frappe.db.get_value("Account", self.credit_to,
["account_type", "report_type", "account_currency"], as_dict=True)
if account.report_type != "Balance Sheet":
@ -99,7 +100,7 @@ class PurchaseInvoice(BuyingController):
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):
@ -251,7 +252,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(
@ -272,10 +273,10 @@ 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, "account_currency")
account_currency = get_account_currency(tax.account_head)
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
gl_entries.append(
self.get_gl_dict({
"account": tax.account_head,
@ -301,7 +302,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, "account_currency")
account_currency = get_account_currency(item.expense_account)
gl_entries.append(
self.get_gl_dict({
"account": item.expense_account,
@ -363,8 +364,8 @@ 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")
write_off_account_currency = get_account_currency(self.write_off_account)
gl_entries.append(
self.get_gl_dict({
"account": self.credit_to,

View File

@ -10,7 +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
from erpnext.exceptions import InvalidCurrency
test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"]
@ -219,7 +219,7 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.load_from_db()
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_type='Purchase Invoice'
where reference_type='Purchase Invoice'
and reference_name=%s and debit_in_account_currency=300""", pi.name))
self.assertEqual(pi.outstanding_amount, 1212.30)
@ -237,17 +237,17 @@ class TestPurchaseInvoice(unittest.TestCase):
existing_purchase_cost = frappe.db.sql("""select sum(ifnull(base_net_amount, 0))
from `tabPurchase Invoice Item` where project_name = '_Test Project' and docstatus=1""")
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
pi = make_purchase_invoice(currency="USD", conversion_rate=60, project_name="_Test Project")
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
existing_purchase_cost + 15000)
pi1 = make_purchase_invoice(qty=10, project_name="_Test Project")
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
existing_purchase_cost + 15500)
pi1.cancel()
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
existing_purchase_cost + 15000)
pi.cancel()
@ -278,14 +278,14 @@ 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",
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,
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)
@ -308,16 +308,16 @@ class TestPurchaseInvoice(unittest.TestCase):
"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

View File

@ -11,6 +11,7 @@ from erpnext.controllers.stock_controller import update_gl_entries_after
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -186,7 +187,7 @@ class SalesInvoice(SellingController):
pos = self.set_pos_fields(for_validate)
if not self.debit_to:
self.debit_to = get_party_account(self.company, self.customer, "Customer")
self.debit_to = get_party_account("Customer", self.customer, self.company)
if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
@ -531,7 +532,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, "account_currency")
account_currency = get_account_currency(tax.account_head)
gl_entries.append(
self.get_gl_dict({
"account": tax.account_head,
@ -547,7 +548,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, "account_currency")
account_currency = get_account_currency(item.income_account)
gl_entries.append(
self.get_gl_dict({
"account": item.income_account,
@ -566,7 +567,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")
bank_account_currency = get_account_currency(self.cash_bank_account)
# POS, make payment entries
gl_entries.append(
self.get_gl_dict({
@ -594,7 +595,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, "account_currency")
write_off_account_currency = get_account_currency(self.write_off_account)
gl_entries.append(
self.get_gl_dict({

View File

@ -7,8 +7,7 @@ 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
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
class TestSalesInvoice(unittest.TestCase):
def make(self):
@ -842,13 +841,13 @@ 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",
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,
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)
@ -871,7 +870,7 @@ class TestSalesInvoice(unittest.TestCase):
"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])
@ -883,38 +882,38 @@ class TestSalesInvoice(unittest.TestCase):
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",
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",
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",
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",
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",
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):

View File

@ -10,9 +10,9 @@ from frappe.defaults import get_user_permissions
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
from erpnext.exceptions import InvalidAccountCurrency
class InvalidCurrency(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
class DuplicatePartyAccountError(frappe.ValidationError): pass
@frappe.whitelist()
def get_party_details(party=None, account=None, party_type="Customer", company=None,
@ -142,7 +142,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
}
if party:
account = get_party_account(company, party, party_type)
account = get_party_account(party_type, party, company)
account_fieldname = "debit_to" if party_type=="Customer" else "credit_to"
@ -153,44 +153,6 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
}
return out
def validate_accounting_currency(party):
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"]):
@ -199,13 +161,13 @@ def get_company_currency():
return company_currency
@frappe.whitelist()
def get_party_account(company, party, party_type):
def get_party_account(party_type, party, company):
"""Returns the account for the given `party`.
Will first search in party (Customer / Supplier) record, if not found,
will search in group (Customer Group / Supplier Type),
finally will return default."""
if not company:
frappe.throw(_("Please select company first."))
frappe.throw(_("Please select a Company"))
if party:
account = frappe.db.get_value("Party Account",
@ -223,6 +185,42 @@ def get_party_account(company, party, party_type):
return account
def get_party_account_currency(party_type, party, company):
def generator():
party_account = get_party_account(party_type, party, company)
return frappe.db.get_value("Account", party_account, "account_currency")
return frappe.local_cache("party_account_currency", (party_type, party, company), generator)
def get_party_gle_currency(party_type, party, company):
def generator():
existing_gle_currency = frappe.db.sql("""select account_currency from `tabGL Entry`
where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
limit 1""", { "company": company, "party_type": party_type, "party": party })
return existing_gle_currency[0][0] if existing_gle_currency else None
return frappe.local_cache("party_gle_currency", (party_type, party, company), generator)
def validate_party_gle_currency(party_type, party, company):
"""Validate party account currency with existing GL Entry's currency"""
party_account_currency = get_party_account_currency(party_type, party, company)
existing_gle_currency = get_party_gle_currency(party_type, party, company)
if existing_gle_currency and party_account_currency != existing_gle_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(party_type, party, existing_gle_currency), InvalidAccountCurrency)
def validate_party_accounts(doc):
companies = []
for account in doc.get("accounts"):
if account.company in companies:
frappe.throw(_("There can only be 1 Account per Company in {0} {1}").format(doc.doctype, doc.name),
DuplicatePartyAccountError)
else:
companies.append(account.company)
@frappe.whitelist()
def get_due_date(posting_date, party_type, party, company):
"""Set Due Date = Posting Date + Credit Days"""

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import flt, getdate, cstr
from frappe import _
from erpnext.accounts.utils import get_account_currency
def execute(filters=None):
account_details = {}
@ -12,10 +13,10 @@ def execute(filters=None):
account_details.setdefault(acc.name, acc)
validate_filters(filters, account_details)
validate_party(filters)
filters = set_account_currency(filters)
filters = set_account_currency(filters)
columns = get_columns(filters)
@ -46,49 +47,49 @@ 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 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")
account_currency = get_account_currency(filters.account)
elif filters.get("party"):
gle_currency = frappe.db.get_value("GL Entry", {"party_type": filters.party_type,
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",
_("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"
]
return columns
def get_result(filters, account_details):
@ -101,21 +102,21 @@ def get_result(filters, account_details):
return result
def get_gl_entries(filters):
select_fields = """, sum(ifnull(debit_in_account_currency, 0)) as debit_in_account_currency,
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,
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(select_fields=select_fields, conditions=get_conditions(filters),
.format(select_fields=select_fields, conditions=get_conditions(filters),
group_by_condition=group_by_condition), filters, as_dict=1)
return gl_entries
@ -135,7 +136,7 @@ def get_conditions(filters):
if filters.get("party"):
conditions.append("party=%(party)s")
if not (filters.get("account") or filters.get("party") or filters.get("group_by_account")):
conditions.append("posting_date >=%(from_date)s")
@ -160,7 +161,7 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
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
@ -170,7 +171,7 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
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)"),
@ -179,10 +180,10 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
# Total debit and credit between from and to date
if total_debit or total_credit:
data.append({
"account": "'" + _("Totals") + "'",
"debit": total_debit,
"account": "'" + _("Totals") + "'",
"debit": total_debit,
"credit": total_credit,
"debit_in_account_currency": total_debit_in_account_currency,
"debit_in_account_currency": total_debit_in_account_currency,
"credit_in_account_currency": total_credit_in_account_currency
})
@ -191,7 +192,7 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
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)"),
closing, closing_in_account_currency))
@ -216,38 +217,38 @@ def initialize_gle_map(gl_entries):
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)
total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
return opening, total_debit, total_credit, opening_in_account_currency, \
total_debit_in_account_currency, total_credit_in_account_currency, gle_map
@ -258,27 +259,27 @@ def get_balance_row(label, balance, balance_in_account_currency=None):
"debit": 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, filters):
result = []
for d in data:
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"),
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

@ -9,6 +9,9 @@ from frappe import throw, _
from frappe.utils import formatdate
import frappe.desk.reportview
# imported to enable erpnext.accounts.utils.get_account_currency
from erpnext.accounts.doctype.account.account import get_account_currency
class FiscalYearError(frappe.ValidationError): pass
class BudgetError(frappe.ValidationError): pass
@ -94,8 +97,8 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, in_acco
select name from `tabAccount` ac where ac.name = gle.account
and ac.lft >= %s and ac.rgt <= %s
)""" % (acc.lft, acc.rgt))
# If group and currency same as company,
# If group and currency same as company,
# always return balance based on debit and credit in company currency
if acc.account_currency == frappe.db.get_value("Company", acc.company, "default_currency"):
in_account_currency = False

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +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
from erpnext.accounts.party import validate_party_accounts
class Supplier(TransactionBase):
def get_feed(self):
@ -45,9 +45,8 @@ 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)
validate_party_accounts(self)
def get_contacts(self,nm):
if nm:
@ -96,14 +95,14 @@ def get_dashboard_info(supplier):
billing_this_year = frappe.db.sql("""
select sum(ifnull(credit_in_account_currency, 0)) - sum(ifnull(debit_in_account_currency, 0))
from `tabGL Entry`
where voucher_type='Purchase Invoice' and party_type = 'Supplier'
and party=%s and fiscal_year = %s""",
where voucher_type='Purchase Invoice' and party_type = 'Supplier'
and party=%s and fiscal_year = %s""",
(supplier, frappe.db.get_default("fiscal_year")))
total_unpaid = frappe.db.sql("""select sum(outstanding_amount)
from `tabPurchase Invoice`
where supplier=%s and docstatus = 1""", supplier)
out["billing_this_year"] = billing_this_year[0][0] if billing_this_year else 0
out["total_unpaid"] = total_unpaid[0][0] if total_unpaid else 0

View File

@ -1,22 +1,21 @@
[
{
"doctype": "Supplier",
"supplier_name": "_Test Supplier",
"doctype": "Supplier",
"supplier_name": "_Test Supplier",
"supplier_type": "_Test Supplier Type"
},
{
"doctype": "Supplier",
"supplier_name": "_Test Supplier 1",
"doctype": "Supplier",
"supplier_name": "_Test Supplier 1",
"supplier_type": "_Test Supplier Type"
},
{
"doctype": "Supplier",
"supplier_name": "_Test Supplier USD",
"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

@ -6,16 +6,15 @@ import frappe
from frappe import _, throw
from frappe.utils import today, flt, cint
from erpnext.setup.utils import get_company_currency, get_exchange_rate
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year, get_account_currency
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
from erpnext.controllers.sales_and_purchase_return import validate_return
from erpnext.accounts.party import get_party_account_currency, validate_party_gle_currency
from erpnext.exceptions import CustomerFrozen, InvalidCurrency
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)
@ -220,7 +219,7 @@ class AccountsController(TransactionBase):
gl_dict.update(args)
if not account_currency:
account_currency = frappe.db.get_value("Account", gl_dict.account, "account_currency")
account_currency = get_account_currency(gl_dict.account)
if self.doctype != "Journal Entry":
self.validate_account_currency(gl_dict.account, account_currency)
@ -427,8 +426,7 @@ class AccountsController(TransactionBase):
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
party_account_currency = get_party_account_currency(party_type, party, self.company)
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}")

View File

@ -7,8 +7,8 @@ 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
from erpnext.accounts.party import get_party_account_currency
subject_field = "title"
sender_field = "contact_email"
@ -180,9 +180,10 @@ 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)
company_currency = frappe.db.get_value("Company", quotation.company, "default_currency")
party_account_currency = frappe.db.get_value("Customer", quotation.customer, "party_account_currency")
party_account_currency = get_party_account_currency("Customer", quotation.customer, quotation.company)
if company_currency == party_account_currency:
exchange_rate = 1
else:
@ -190,7 +191,7 @@ def make_quotation(source_name, target_doc=None):
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")

7
erpnext/exceptions.py Normal file
View File

@ -0,0 +1,7 @@
from __future__ import unicode_literals
import frappe
# accounts
class CustomerFrozen(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass

View File

@ -63,56 +63,3 @@ def execute():
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, filters={"docstatus": 0})
for p in parties:
party = frappe.get_doc(dt, p.name)
party_accounts = []
for company in company_list:
# Get party GL Entries
party_gle = frappe.db.get_value("GL Entry", {"party_type": dt, "party": p.name,
"company": company.name}, ["account", "account_currency", "name"], as_dict=True)
# set party account currency
if party_gle:
party.party_account_currency = party_gle.account_currency
elif 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_for_company = False
for d in party.get("accounts"):
if d.company == company.name:
account_currency = frappe.db.get_value("Account", d.account, "account_currency")
if account_currency == party.party_account_currency:
party_accounts.append({
"company": d.company,
"account": d.account
})
party_account_exists_for_company = True
break
if not party_account_exists_for_company:
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_accounts.append({
"company": company.name,
"account": party_account
})
party.set("accounts", party_accounts)
party.flags.ignore_mandatory = True
party.save()

File diff suppressed because it is too large Load Diff

View File

@ -7,11 +7,10 @@ from frappe.model.naming import make_autoname
from frappe import _, msgprint, throw
import frappe.defaults
from frappe.utils import flt
from frappe.desk.reportview import build_match_conditions
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
from erpnext.accounts.party import validate_party_accounts
class Customer(TransactionBase):
def get_feed(self):
@ -33,8 +32,7 @@ class Customer(TransactionBase):
def validate(self):
self.flags.is_new_doc = self.is_new()
validate_accounting_currency(self)
validate_party_account(self)
validate_party_accounts(self)
def update_lead_status(self):
if self.lead_name:

View File

@ -7,9 +7,7 @@ import frappe
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
from erpnext.exceptions import CustomerFrozen
test_ignore = ["Price List"]
@ -37,9 +35,9 @@ class TestCustomer(unittest.TestCase):
make_test_records("Address")
make_test_records("Contact")
frappe.db.set_value("Contact", "_Test Contact For _Test Customer-_Test Customer",
frappe.db.set_value("Contact", "_Test Contact For _Test Customer-_Test Customer",
"is_primary_contact", 1)
details = get_party_details("_Test Customer")
for key, value in to_check.iteritems():
@ -68,25 +66,15 @@ class TestCustomer(unittest.TestCase):
{"comment_doctype": "Customer", "comment_docname": "_Test Customer 1 Renamed"}), comment.name)
frappe.rename_doc("Customer", "_Test Customer 1 Renamed", "_Test Customer 1")
def test_freezed_customer(self):
frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 1)
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
so = make_sales_order(do_not_save= True)
self.assertRaises(CustomerFrozen, so.save)
frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 0)
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

@ -33,7 +33,6 @@
"customer_type": "Individual",
"doctype": "Customer",
"territory": "_Test Territory",
"party_account_currency": "USD",
"accounts": [{
"company": "_Test Company",
"account": "_Test Receivable USD - _TC"

View File

@ -7,6 +7,8 @@ import frappe.permissions
import unittest
from erpnext.selling.doctype.sales_order.sales_order \
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
from erpnext.accounts.doctype.journal_entry.test_journal_entry \
import make_journal_entry
from frappe.tests.test_permissions import set_user_permission_doctypes

View File

@ -46,13 +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."))
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
@ -208,7 +208,7 @@ class Company(Document):
# clear default accounts, warehouses from item
if warehouses:
for f in ["default_warehouse", "website_warehouse"]:
frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)"""
% (f, f, ', '.join(['%s']*len(warehouses))), tuple(warehouses))
@ -257,3 +257,7 @@ def get_name_with_abbr(name, company):
parts.append(company_abbr)
return " - ".join(parts)
def get_company_currency(company):
return frappe.local_cache("company_currency", company,
lambda: frappe.db.get_value("Company", company, "default_currency"))

View File

@ -10,6 +10,7 @@ from frappe import _
import frappe.defaults
from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.utils import get_account_currency
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -307,9 +308,9 @@ 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)
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]["name"],
"against": stock_rbnb,
@ -319,7 +320,7 @@ class PurchaseReceipt(BuyingController):
}, warehouse_account[d.warehouse]["account_currency"]))
# stock received but not billed
stock_rbnb_currency = frappe.db.get_value("Account", stock_rbnb, "account_currency")
stock_rbnb_currency = get_account_currency(stock_rbnb)
gl_entries.append(self.get_gl_dict({
"account": stock_rbnb,
"against": warehouse_account[d.warehouse]["name"],