Merge pull request #10426 from nabinhait/payment_entry_deductions
Unallocated and difference amount calculation in Payment Entry
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 12e46c4..dc37574 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -644,16 +644,9 @@
if(frm.doc.party) {
var party_amount = frm.doc.payment_type=="Receive" ?
frm.doc.paid_amount : frm.doc.received_amount;
-
- var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
- function(d) { return flt(d.amount) }));
if(frm.doc.total_allocated_amount < party_amount) {
- if(frm.doc.payment_type == "Receive") {
- unallocated_amount = party_amount - (frm.doc.total_allocated_amount - total_deductions);
- } else {
- unallocated_amount = party_amount - (frm.doc.total_allocated_amount + total_deductions);
- }
+ unallocated_amount = party_amount - frm.doc.total_allocated_amount;
}
}
frm.set_value("unallocated_amount", unallocated_amount);
@@ -672,11 +665,10 @@
difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount);
}
- $.each(frm.doc.deductions || [], function(i, d) {
- if(d.amount) difference_amount -= flt(d.amount);
- })
+ var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
+ function(d) { return flt(d.amount) }));
- frm.set_value("difference_amount", difference_amount);
+ frm.set_value("difference_amount", difference_amount - total_deductions);
frm.events.hide_unhide_fields(frm);
},
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 9832c05..908e58e 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -281,13 +281,8 @@
if self.party:
party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
- total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
-
if self.total_allocated_amount < party_amount:
- if self.payment_type == "Receive":
- self.unallocated_amount = party_amount - (self.total_allocated_amount - total_deductions)
- else:
- self.unallocated_amount = party_amount - (self.total_allocated_amount + total_deductions)
+ self.unallocated_amount = party_amount - self.total_allocated_amount
def set_difference_amount(self):
base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
@@ -302,11 +297,10 @@
else:
self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
- for d in self.get("deductions"):
- if d.amount:
- self.difference_amount -= flt(d.amount)
+ total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
- self.difference_amount = flt(self.difference_amount, self.precision("difference_amount"))
+ self.difference_amount = flt(self.difference_amount - total_deductions,
+ self.precision("difference_amount"))
def clear_unallocated_reference_document_rows(self):
self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]}))
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 0316cca..60be20d 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -267,3 +267,65 @@
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)
+
+ def test_payment_entry_write_off_difference(self):
+ si = create_sales_invoice()
+ pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = "2016-01-01"
+ pe.received_amount = pe.paid_amount = 110
+ pe.insert()
+
+ self.assertEqual(pe.unallocated_amount, 10)
+
+ pe.received_amount = pe.paid_amount = 95
+ pe.append("deductions", {
+ "account": "_Test Write Off - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 5
+ })
+ pe.save()
+
+ self.assertEqual(pe.unallocated_amount, 0)
+ self.assertEqual(pe.difference_amount, 0)
+
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["Debtors - _TC", 0, 100, si.name],
+ ["_Test Cash - _TC", 95, 0, None],
+ ["_Test Write Off - _TC", 5, 0, None]
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ def test_payment_entry_exchange_gain_loss(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 = 55
+
+ pe.append("deductions", {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": -500
+ })
+ pe.save()
+
+ self.assertEqual(pe.unallocated_amount, 0)
+ self.assertEqual(pe.difference_amount, 0)
+
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Receivable USD - _TC", 0, 5000, si.name],
+ ["_Test Bank USD - _TC", 5500, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 0, 500, 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)
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js
new file mode 100644
index 0000000..7dea76d
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js
@@ -0,0 +1,51 @@
+QUnit.module('Payment Entry');
+
+QUnit.test("test payment entry", function(assert) {
+ assert.expect(6);
+ let done = assert.async();
+ frappe.run_serially([
+ () => {
+ return frappe.tests.make('Sales Invoice', [
+ {customer: 'Test Customer 1'},
+ {items: [
+ [
+ {'qty': 1},
+ {'rate': 101},
+ {'item_code': 'Test Product 1'},
+ ]
+ ]}
+ ]);
+ },
+ () => cur_frm.save(),
+ () => frappe.tests.click_button('Submit'),
+ () => frappe.tests.click_button('Yes'),
+ () => frappe.timeout(0.5),
+ () => frappe.tests.click_button('Close'),
+ () => frappe.timeout(0.5),
+ () => frappe.click_button('Make'),
+ () => frappe.click_link('Payment', 1),
+ () => frappe.timeout(2),
+ () => {
+ assert.equal(frappe.get_route()[1], 'Payment Entry',
+ 'made payment entry');
+ assert.equal(cur_frm.doc.party, 'Test Customer 1',
+ 'customer set in payment entry');
+ assert.equal(cur_frm.doc.paid_amount, 101,
+ 'paid amount set in payment entry');
+ assert.equal(cur_frm.doc.references[0].allocated_amount, 101,
+ 'amount allocated against sales invoice');
+ },
+ () => cur_frm.set_value('paid_amount', 100),
+ () => {
+ cur_frm.doc.references[0].allocated_amount = 101;
+ },
+ () => frappe.click_button('Write Off Difference Amount'),
+ () => {
+ assert.equal(cur_frm.doc.difference_amount, 0,
+ 'difference amount is zero');
+ assert.equal(cur_frm.doc.deductions[0].amount, 1,
+ 'Write off amount = 1');
+ },
+ () => done()
+ ]);
+});
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
index a4ef0ca..0c76343 100644
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
@@ -25,5 +25,4 @@
() => frappe.timeout(0.3),
() => done()
]);
-});
-
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js
new file mode 100644
index 0000000..133f136
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js
@@ -0,0 +1,67 @@
+QUnit.module('Payment Entry');
+
+QUnit.test("test payment entry", function(assert) {
+ assert.expect(8);
+ let done = assert.async();
+ frappe.run_serially([
+ () => {
+ return frappe.tests.make('Sales Invoice', [
+ {customer: 'Test Customer 1'},
+ {company: '_Test Company'},
+ {currency: 'INR'},
+ {selling_price_list: '_Test Price List'},
+ {items: [
+ [
+ {'qty': 1},
+ {'item_code': 'Test Product 1'},
+ ]
+ ]}
+ ]);
+ },
+ () => frappe.timeout(1),
+ () => cur_frm.save(),
+ () => frappe.tests.click_button('Submit'),
+ () => frappe.tests.click_button('Yes'),
+ () => frappe.timeout(1.5),
+ () => frappe.click_button('Close'),
+ () => frappe.timeout(0.5),
+ () => frappe.click_button('Make'),
+ () => frappe.timeout(1),
+ () => frappe.click_link('Payment'),
+ () => frappe.timeout(2),
+ () => cur_frm.set_value("paid_to", "_Test Cash - _TC"),
+ () => frappe.timeout(0.5),
+ () => {
+ assert.equal(frappe.get_route()[1], 'Payment Entry', 'made payment entry');
+ assert.equal(cur_frm.doc.party, 'Test Customer 1', 'customer set in payment entry');
+ assert.equal(cur_frm.doc.paid_from, 'Debtors - _TC', 'customer account set in payment entry');
+ assert.equal(cur_frm.doc.paid_amount, 100, 'paid amount set in payment entry');
+ assert.equal(cur_frm.doc.references[0].allocated_amount, 100,
+ 'amount allocated against sales invoice');
+ },
+ () => cur_frm.set_value('paid_amount', 95),
+ () => frappe.timeout(1),
+ () => {
+ frappe.model.set_value("Payment Entry Reference",
+ cur_frm.doc.references[0].name, "allocated_amount", 100);
+ },
+ () => frappe.timeout(.5),
+ () => {
+ assert.equal(cur_frm.doc.difference_amount, 5, 'difference amount is 5');
+ },
+ () => {
+ frappe.db.set_value("Company", "_Test Company", "write_off_account", "_Test Write Off - _TC");
+ frappe.timeout(1);
+ frappe.db.set_value("Company", "_Test Company",
+ "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC");
+ },
+ () => frappe.timeout(1),
+ () => frappe.click_button('Write Off Difference Amount'),
+ () => frappe.timeout(2),
+ () => {
+ assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero');
+ assert.equal(cur_frm.doc.deductions[0].amount, 5, 'Write off amount = 5');
+ },
+ () => done()
+ ]);
+});
\ No newline at end of file
diff --git a/erpnext/tests/ui/make_fixtures.js b/erpnext/tests/ui/make_fixtures.js
index 0c5b4be..f817c65 100644
--- a/erpnext/tests/ui/make_fixtures.js
+++ b/erpnext/tests/ui/make_fixtures.js
@@ -205,6 +205,18 @@
{title: "Test Term 2"}
]
},
+ "Item Price": {
+ "ITEM-PRICE-00001": [
+ {item_code: 'Test Product 1'},
+ {price_list: '_Test Price List'},
+ {price_list_rate: 100}
+ ],
+ "ITEM-PRICE-00002": [
+ {item_code: 'Test Product 2'},
+ {price_list: '_Test Price List'},
+ {price_list_rate: 200}
+ ]
+ }
});
diff --git a/erpnext/tests/ui/tests.txt b/erpnext/tests/ui/tests.txt
index 313554b..6017f6f 100644
--- a/erpnext/tests/ui/tests.txt
+++ b/erpnext/tests/ui/tests.txt
@@ -125,4 +125,5 @@
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
-erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
\ No newline at end of file
+erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
+erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js