From 9db1b223da36d35c4096e33dd0ed076593a50f7e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jun 2016 12:37:53 +0530 Subject: [PATCH] Payment Entry: Test cases and default remarks --- .../accounts/doctype/account/test_account.py | 2 + erpnext/accounts/doctype/gl_entry/gl_entry.py | 2 +- .../doctype/payment_entry/payment_entry.py | 42 +++- .../payment_entry/test_payment_entry.py | 179 ++++++++++++++---- erpnext/controllers/accounts_controller.py | 2 +- 5 files changed, 186 insertions(+), 41 deletions(-) diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 4580d02dd4..5744721875 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -12,6 +12,7 @@ def _make_test_records(verbose): ["_Test Bank", "Bank Accounts", 0, "Bank", None], ["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"], ["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"], + ["_Test Cash", "Cash In Hand", 0, "Cash", None], ["_Test Account Stock Expenses", "Direct Expenses", 1, None, None], ["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None], @@ -32,6 +33,7 @@ def _make_test_records(verbose): ["_Test Account CST", "Direct Expenses", 0, "Tax", None], ["_Test Account Discount", "Direct Expenses", 0, None, None], ["_Test Write Off", "Indirect Expenses", 0, None, None], + ["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None], # related to Account Inventory Integration ["_Test Account Stock In Hand", "Current Assets", 0, None, None], diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 1f95fb8daf..b90cd2308c 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -38,7 +38,7 @@ class GLEntry(Document): self.against_voucher) def check_mandatory(self): - mandatory = ['account','remarks','voucher_type','voucher_no','company'] + mandatory = ['account','voucher_type','voucher_no','company'] for k in mandatory: if not self.get(k): frappe.throw(_("{0} is required").format(_(self.meta.get_label(k)))) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index fc28c5473f..7738563908 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -44,7 +44,7 @@ class PaymentEntry(AccountsController): self.clear_unallocated_reference_document_rows() self.set_title() self.validate_transaction_reference() - + self.set_remarks() def on_submit(self): self.make_gl_entries() @@ -137,7 +137,7 @@ class PaymentEntry(AccountsController): frappe.throw(_("Account Type for {0} must be {1}").format(comma_or(account_types))) def set_exchange_rate(self): - if self.paid_from: + if self.paid_from and not self.source_exchange_rate: if self.paid_from_account_currency == self.company_currency: self.source_exchange_rate = 1 elif self.payment_type in ("Pay", "Internal Transfer"): @@ -146,7 +146,7 @@ class PaymentEntry(AccountsController): self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency, self.company_currency) - if self.paid_to: + if self.paid_to and not self.target_exchange_rate: self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency, self.company_currency) @@ -204,7 +204,6 @@ class PaymentEntry(AccountsController): self.total_allocated_amount, self.base_total_allocated_amount = 0, 0 for d in self.get("references"): if d.allocated_amount: - print d.allocated_amount, d.outstanding_amount if d.reference_doctype not in ("Sales Order", "Purchase Order") \ and d.allocated_amount > d.outstanding_amount: frappe.throw(_("Row #{0}: Allocated amount cannot be greater than outstanding amount") @@ -226,7 +225,7 @@ class PaymentEntry(AccountsController): base_unallocated_amount = self.unallocated_amount * \ (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate) - base_party_amount = self.base_total_allocated_amount + base_unallocated_amount + base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount) if self.payment_type == "Receive": self.difference_amount = base_party_amount - self.base_received_amount @@ -259,6 +258,37 @@ class PaymentEntry(AccountsController): if not self.reference_no or not self.reference_date: frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) + def set_remarks(self): + if self.remarks: return + + if self.payment_type=="Internal Transfer": + remarks = [_("Amount {0} {1} transferred from {2} to {3}") + .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] + else: + + remarks = [_("Amount {0} {1} {2} {3}").format( + self.party_account_currency, + self.paid_amount if self.payment_type=="Receive" else self.received_amount, + _("received from") if self.payment_type=="Receive" else _("to"), self.party + )] + + if self.reference_no: + remarks.append(_("Transaction reference no {0} dated {1}") + .format(self.reference_no, self.reference_date)) + + if self.payment_type in ["Receive", "Pay"]: + for d in self.get("references"): + if d.allocated_amount: + remarks.append(_("Amount {0} {1} against {2} {3}").format(self.party_account_currency, + d.allocated_amount, d.reference_doctype, d.reference_name)) + + for d in self.get("deductions"): + if d.amount: + remarks.append(_("Amount {0} {1} deducted against {2}") + .format(self.company_currency, d.amount, d.account)) + + self.set("remarks", "\n".join(remarks)) + def make_gl_entries(self, cancel=0, adv_adj=0): gl_entries = [] self.add_party_gl_entries(gl_entries) @@ -520,7 +550,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= received_amount = outstanding_amount if bank_amount: paid_amount = bank_amount - + pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type pe.company = doc.company diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 30ee18edc5..624f405620 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -5,50 +5,163 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.accounts.doctype.sales_order.test_sales_order import make_sales_order -from erpnext.accounts.doctype.payment_entry.payment_entry import make_payment_entry +from frappe.utils import flt, nowdate +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice + +test_dependencies = ["Item"] class TestPaymentEntry(unittest.TestCase): def test_payment_entry_against_order(self): so = make_sales_order() - pe = make_payment_entry("Sales Order", so.name) - pe.paid_to = "_Test Bank - _TC" + pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC") + pe.paid_from = "Debtors - _TC" pe.insert() pe.submit() - expected_gle = { - "_Test Bank - _TC": { - "account_currency": "INR", - "debit": 1000, - "debit_in_account_currency": 1000, - "credit": 0, - "credit_in_account_currency": 0, - "against_voucher": None - }, - "_Test Receivable - _TC": { - "account_currency": "INR", - "debit": 0, - "debit_in_account_currency": 0, - "credit": 1000, - "credit_in_account_currency": 1000, - "against_voucher": so.name - } - } + expected_gle = dict((d[0], d) for d in [ + ["Debtors - _TC", 0, 1000, so.name], + ["_Test Cash - _TC", 1000.0, 0, None] + ]) + + self.validate_gl_entries(pe.name, expected_gle) + + so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid") + self.assertEqual(so_advance_paid, 1000) + + pe.cancel() + + self.assertFalse(self.get_gle(pe.name)) + + so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid") + self.assertEqual(so_advance_paid, 0) + + def test_payment_entry_against_si_usd_to_usd(self): + si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50) + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.target_exchange_rate = 50 + pe.insert() + pe.submit() + + expected_gle = dict((d[0], d) for d in [ + ["_Test Receivable USD - _TC", 0, 5000, si.name], + ["_Test Bank USD - _TC", 5000.0, 0, None] + ]) + + self.validate_gl_entries(pe.name, expected_gle) + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + + pe.cancel() + self.assertFalse(self.get_gle(pe.name)) + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 100) + + def test_payment_entry_against_pi(self): + pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", + currency="USD", conversion_rate=50) + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.target_exchange_rate = 50 + pe.insert() + pe.submit() + + expected_gle = dict((d[0], d) for d in [ + ["_Test Payable USD - _TC", 12500, 0, pi.name], + ["_Test Bank USD - _TC", 0, 12500, None] + ]) + + self.validate_gl_entries(pe.name, expected_gle) + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + + def test_payment_entry_against_si_usd_to_inr(self): + si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50) + pe = get_payment_entry("Sales Invoice", si.name, party_amount=20, + bank_account="_Test Bank - _TC", bank_amount=900) + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + + self.assertEqual(pe.difference_amount, 100) + + pe.append("deductions", { + "account": "_Test Exchange Gain/Loss - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 100 + }) + pe.insert() + pe.submit() + + expected_gle = dict((d[0], d) for d in [ + ["_Test Receivable USD - _TC", 0, 1000, si.name], + ["_Test Bank - _TC", 900, 0, None], + ["_Test Exchange Gain/Loss - _TC", 100.0, 0, None], + ]) + + self.validate_gl_entries(pe.name, expected_gle) + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 80) + + def test_internal_transfer_usd_to_inr(self): + pe = frappe.new_doc("Payment Entry") + pe.payment_type = "Internal Transfer" + pe.company = "_Test Company" + pe.paid_from = "_Test Bank USD - _TC" + pe.paid_to = "_Test Bank - _TC" + pe.paid_amount = 100 + pe.source_exchange_rate = 50 + pe.received_amount = 4500 + pe.reference_no = "2" + pe.reference_date = nowdate() + + pe.setup_party_account_field() + pe.set_missing_values() + pe.set_exchange_rate() + pe.set_amounts() + + self.assertEquals(pe.difference_amount, 500) + + pe.append("deductions", { + "account": "_Test Exchange Gain/Loss - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 500 + }) + + pe.insert() + pe.submit() + + expected_gle = dict((d[0], d) for d in [ + ["_Test Bank USD - _TC", 0, 5000, None], + ["_Test Bank - _TC", 4500, 0, None], + ["_Test Exchange Gain/Loss - _TC", 500.0, 0, None], + ]) self.validate_gl_entries(pe.name, expected_gle) - so.load_from_db() def validate_gl_entries(self, voucher_no, expected_gle): - gl_entries = frappe.db.sql("""select account, account_currency, debit, credit, - debit_in_account_currency, credit_in_account_currency, against_voucher + gl_entries = self.get_gle(voucher_no) + + self.assertTrue(gl_entries) + + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_gle[gle.account][0], gle.account) + self.assertEquals(expected_gle[gle.account][1], gle.debit) + self.assertEquals(expected_gle[gle.account][2], gle.credit) + self.assertEquals(expected_gle[gle.account][3], gle.against_voucher) + + def get_gle(self, voucher_no): + return frappe.db.sql("""select account, debit, credit, against_voucher from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s order by account asc""", voucher_no, as_dict=1) - - self.assertTrue(gl_entries) - - 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_gle[gle.account][field], gle[field]) - diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0e37e1121d..2b79219f30 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -462,7 +462,7 @@ class AccountsController(TransactionBase): formatted_order_total = fmt_money(order_total, precision=self.precision("base_grand_total"), currency=advance.account_currency) - if self.currency == self.company_currency: + if self.currency == self.company_currency and advance_paid > order_total: frappe.throw(_("Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2})") .format(formatted_advance_paid, self.name, formatted_order_total))