[fix] added validation to match account currency with existing gle and test case

This commit is contained in:
Anand Doshi 2015-09-30 16:41:15 +05:30
parent 248c867a2c
commit 4945b94950
10 changed files with 122 additions and 59 deletions

View File

@ -6,12 +6,10 @@ import frappe
from frappe import _ from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.accounts.party import get_party_account_currency from erpnext.accounts.party import validate_party_gle_currency, get_party_account_currency
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
from erpnext.setup.doctype.company.company import get_company_currency
class CustomerFrozen(frappe.ValidationError): pass from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
class InvalidCurrency(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
class GLEntry(Document): class GLEntry(Document):
def validate(self): def validate(self):
@ -103,16 +101,16 @@ class GLEntry(Document):
frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen) frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen)
def validate_currency(self): def validate_currency(self):
company_currency = frappe.db.get_value("Company", self.company, "default_currency") company_currency = get_company_currency(self.company)
account_currency = get_account_currency(self.account) account_currency = get_account_currency(self.account)
if not self.account_currency: if not self.account_currency:
self.account_currency = company_currency self.account_currency = company_currency
if account_currency != self.account_currency: if account_currency != self.account_currency:
frappe.throw(_("Accounting Entry for {0} can only be made in currency: {1}") frappe.throw(_("Accounting Entry for {0} can only be made in currency: {1}")
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency) .format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
if self.party_type and self.party: if self.party_type and self.party:
party_account_currency = get_party_account_currency(self.party_type, self.party, self.company) party_account_currency = get_party_account_currency(self.party_type, self.party, self.company)
@ -120,6 +118,8 @@ class GLEntry(Document):
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}") 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): def validate_balance_type(account, adv_adj=False):
if not adv_adj and account: if not adv_adj and account:
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be") balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import unittest, frappe import unittest, frappe
from frappe.utils import flt from frappe.utils import flt
from erpnext.accounts.utils import get_actual_expense, BudgetError, get_fiscal_year from erpnext.accounts.utils import get_actual_expense, BudgetError, get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency
class TestJournalEntry(unittest.TestCase): class TestJournalEntry(unittest.TestCase):
@ -202,8 +203,6 @@ class TestJournalEntry(unittest.TestCase):
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[gle.account][field], gle[field]) self.assertEquals(expected_values[gle.account][field], gle[field])
# cancel # cancel
jv.cancel() jv.cancel()
@ -212,6 +211,40 @@ class TestJournalEntry(unittest.TestCase):
self.assertFalse(gle) 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): def make_journal_entry(account1, account2, amount, cost_center=None, exchange_rate=1, save=True, submit=False):
jv = frappe.new_doc("Journal Entry") jv = frappe.new_doc("Journal Entry")
jv.posting_date = "2013-02-14" jv.posting_date = "2013-02-14"
@ -231,7 +264,7 @@ def make_journal_entry(account1, account2, amount, cost_center=None, exchange_ra
"cost_center": cost_center, "cost_center": cost_center,
"credit_in_account_currency": 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, "debit_in_account_currency": abs(amount) if amount < 0 else 0,
exchange_rate: exchange_rate "exchange_rate": exchange_rate
} }
]) ])
if save or submit: if save or submit:

View File

@ -10,7 +10,7 @@ from frappe.utils import cint
import frappe.defaults import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records test_records as pr_test_records
from erpnext.controllers.accounts_controller import InvalidCurrency from erpnext.exceptions import InvalidCurrency
test_dependencies = ["Item", "Cost Center"] test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"] test_ignore = ["Serial No"]

View File

@ -7,8 +7,7 @@ import unittest, copy
from frappe.utils import nowdate, add_days, flt from frappe.utils import nowdate, add_days, flt
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.controllers.accounts_controller import InvalidCurrency from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.accounts.doctype.gl_entry.gl_entry import InvalidAccountCurrency
class TestSalesInvoice(unittest.TestCase): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):

View File

@ -10,6 +10,7 @@ from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff
from erpnext.utilities.doctype.address.address import get_address_display from erpnext.utilities.doctype.address.address import get_address_display
from erpnext.utilities.doctype.contact.contact import get_contact_details from erpnext.utilities.doctype.contact.contact import get_contact_details
from erpnext.exceptions import InvalidAccountCurrency
class DuplicatePartyAccountError(frappe.ValidationError): pass class DuplicatePartyAccountError(frappe.ValidationError): pass
@ -191,6 +192,25 @@ def get_party_account_currency(party_type, party, company):
return frappe.local_cache("party_account_currency", (party_type, party, company), generator) 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): def validate_party_accounts(doc):
companies = [] companies = []

View File

@ -10,13 +10,11 @@ from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year, get_ac
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
from erpnext.controllers.sales_and_purchase_return import validate_return from erpnext.controllers.sales_and_purchase_return import validate_return
from erpnext.accounts.party import get_party_account_currency 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") force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
class CustomerFrozen(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
class AccountsController(TransactionBase): class AccountsController(TransactionBase):
def __init__(self, arg1, arg2=None): def __init__(self, arg1, arg2=None):
super(AccountsController, self).__init__(arg1, arg2) super(AccountsController, self).__init__(arg1, arg2)

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

@ -7,7 +7,7 @@ import frappe
import unittest import unittest
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from erpnext.controllers.accounts_controller import CustomerFrozen from erpnext.exceptions import CustomerFrozen
test_ignore = ["Price List"] test_ignore = ["Price List"]

View File

@ -7,6 +7,8 @@ import frappe.permissions
import unittest import unittest
from erpnext.selling.doctype.sales_order.sales_order \ from erpnext.selling.doctype.sales_order.sales_order \
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired 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 from frappe.tests.test_permissions import set_user_permission_doctypes

View File

@ -257,3 +257,7 @@ def get_name_with_abbr(name, company):
parts.append(company_abbr) parts.append(company_abbr)
return " - ".join(parts) return " - ".join(parts)
def get_company_currency(company):
return frappe.local_cache("company_currency", company,
lambda: frappe.db.get_value("Company", company, "default_currency"))