diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index 5dd6259c585..34585ec200d 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -39,10 +39,12 @@ class TestPOSClosingEntry(unittest.TestCase): pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) + pos_inv1.save() pos_inv1.submit() pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.save() pos_inv2.submit() pcv_doc = make_closing_entry_from_opening(opening_entry) @@ -68,6 +70,7 @@ class TestPOSClosingEntry(unittest.TestCase): pos_inv = create_pos_invoice(rate=3500, do_not_submit=1, item_name="Test Item", without_item_code=1) pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) + pos_inv.save() pos_inv.submit() pcv_doc = make_closing_entry_from_opening(opening_entry) @@ -86,10 +89,12 @@ class TestPOSClosingEntry(unittest.TestCase): pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) + pos_inv1.save() pos_inv1.submit() pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.save() pos_inv2.submit() # make return entry of pos_inv2 @@ -111,10 +116,12 @@ class TestPOSClosingEntry(unittest.TestCase): pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) + pos_inv1.save() pos_inv1.submit() pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.save() pos_inv2.submit() pcv_doc = make_closing_entry_from_opening(opening_entry) @@ -165,6 +172,7 @@ class TestPOSClosingEntry(unittest.TestCase): opening_entry = create_opening_entry(pos_profile, test_user.name) pos_inv1 = create_pos_invoice(rate=350, do_not_submit=1, pos_profile=pos_profile.name) pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) + pos_inv1.save() pos_inv1.submit() # if in between a mandatory accounting dimension is added to the POS Profile then @@ -218,11 +226,27 @@ class TestPOSClosingEntry(unittest.TestCase): opening_entry = create_opening_entry(pos_profile, test_user.name) pos_inv = create_pos_invoice( - item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no + item_code=item_code, + qty=5, + rate=300, + use_serial_batch_fields=1, + batch_no=batch_no, + do_not_submit=True, ) + pos_inv.payments[0].amount = pos_inv.grand_total + pos_inv.save() + pos_inv.submit() pos_inv2 = create_pos_invoice( - item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no + item_code=item_code, + qty=5, + rate=300, + use_serial_batch_fields=1, + batch_no=batch_no, + do_not_submit=True, ) + pos_inv2.payments[0].amount = pos_inv2.grand_total + pos_inv2.save() + pos_inv2.submit() batch_qty_with_pos = get_batch_qty(batch_no, "_Test Warehouse - _TC", item_code) self.assertEqual(batch_qty_with_pos, 0.0) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index ab5a4092c33..a8a733ac42c 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -20,6 +20,10 @@ from erpnext.controllers.queries import item_query as _item_query from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +class PartialPaymentValidationError(frappe.ValidationError): + pass + + class POSInvoice(SalesInvoice): # begin: auto-generated types # This code is auto-generated. Do not modify anything in this block. @@ -210,6 +214,7 @@ class POSInvoice(SalesInvoice): self.validate_payment_amount() self.validate_loyalty_transaction() self.validate_company_with_pos_company() + self.validate_full_payment() if self.coupon_code: from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code @@ -477,6 +482,20 @@ class POSInvoice(SalesInvoice): if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points: validate_loyalty_points(self, self.loyalty_points) + def validate_full_payment(self): + invoice_total = flt(self.rounded_total) or flt(self.grand_total) + + if self.docstatus == 1: + if self.is_return and self.paid_amount != invoice_total: + frappe.throw( + msg=_("Partial Payment in POS Invoice is not allowed."), exc=PartialPaymentValidationError + ) + + if self.paid_amount < invoice_total: + frappe.throw( + msg=_("Partial Payment in POS Invoice is not allowed."), exc=PartialPaymentValidationError + ) + def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): if self.get("amended_from"): diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 7b6b8b50543..09c9443bdd9 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -7,7 +7,7 @@ import unittest import frappe from frappe import _ -from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return +from erpnext.accounts.doctype.pos_invoice.pos_invoice import PartialPaymentValidationError, make_sales_return from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.stock.doctype.item.test_item import make_item @@ -313,7 +313,7 @@ class TestPOSInvoice(unittest.TestCase): ) pos.append( - "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1} + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2000, "default": 1} ) pos.insert() @@ -324,6 +324,11 @@ class TestPOSInvoice(unittest.TestCase): # partial return 1 pos_return1.get("items")[0].qty = -1 + pos_return1.set("payments", []) + pos_return1.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": -1000, "default": 1} + ) + pos_return1.paid_amount = -1000 pos_return1.submit() pos_return1.reload() @@ -338,6 +343,11 @@ class TestPOSInvoice(unittest.TestCase): # partial return 2 pos_return2 = make_sales_return(pos.name) + pos_return2.set("payments", []) + pos_return2.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": -1000, "default": 1} + ) + pos_return2.paid_amount = -1000 pos_return2.submit() self.assertEqual(pos_return2.get("items")[0].qty, -1) @@ -373,6 +383,15 @@ class TestPOSInvoice(unittest.TestCase): inv.payments = [] self.assertRaises(frappe.ValidationError, inv.insert) + def test_partial_payment(self): + pos_inv = create_pos_invoice(rate=10000, do_not_save=1) + pos_inv.append( + "payments", + {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 9000}, + ) + pos_inv.insert() + self.assertRaises(PartialPaymentValidationError, pos_inv.submit) + def test_serialized_item_transaction(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item @@ -581,7 +600,13 @@ class TestPOSInvoice(unittest.TestCase): "Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty" ) - inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000) + inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1) + inv.append( + "payments", + {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 10000}, + ) + inv.insert() + inv.submit() lpe = frappe.get_doc( "Loyalty Point Entry", @@ -607,7 +632,13 @@ class TestPOSInvoice(unittest.TestCase): ) # add 10 loyalty points - create_pos_invoice(customer="Test Loyalty Customer", rate=10000) + pos_inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1) + pos_inv.append( + "payments", + {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 10000}, + ) + pos_inv.paid_amount = 10000 + pos_inv.submit() before_lp_details = get_loyalty_program_details_with_points( "Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty" @@ -641,10 +672,12 @@ class TestPOSInvoice(unittest.TestCase): test_user, pos_profile = init_user_and_profile() pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1) pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 270}) + pos_inv.save() pos_inv.submit() pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.save() pos_inv2.submit() consolidate_pos_invoices() @@ -676,6 +709,7 @@ class TestPOSInvoice(unittest.TestCase): "included_in_print_rate": 1, }, ) + pos_inv.save() pos_inv.submit() pos_inv2 = create_pos_invoice(rate=300, qty=2, do_not_submit=1) @@ -692,6 +726,7 @@ class TestPOSInvoice(unittest.TestCase): "included_in_print_rate": 1, }, ) + pos_inv2.save() pos_inv2.submit() consolidate_pos_invoices() @@ -744,6 +779,7 @@ class TestPOSInvoice(unittest.TestCase): "included_in_print_rate": 1, }, ) + pos_inv2.save() pos_inv2.submit() consolidate_pos_invoices() @@ -774,7 +810,10 @@ class TestPOSInvoice(unittest.TestCase): # POS Invoice 1, for the batch without bundle pos_inv1 = create_pos_invoice(item="_BATCH ITEM Test For Reserve", rate=300, qty=15, do_not_save=1) - + pos_inv1.append( + "payments", + {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 4500}, + ) pos_inv1.items[0].batch_no = batch_no pos_inv1.save() pos_inv1.submit() @@ -790,8 +829,14 @@ class TestPOSInvoice(unittest.TestCase): # POS Invoice 2, for the batch with bundle pos_inv2 = create_pos_invoice( - item="_BATCH ITEM Test For Reserve", rate=300, qty=10, batch_no=batch_no + item="_BATCH ITEM Test For Reserve", rate=300, qty=10, batch_no=batch_no, do_not_save=1 ) + pos_inv2.append( + "payments", + {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3000}, + ) + pos_inv2.save() + pos_inv2.submit() pos_inv2.reload() self.assertTrue(pos_inv2.items[0].serial_and_batch_bundle) @@ -826,6 +871,10 @@ class TestPOSInvoice(unittest.TestCase): pos_inv1 = create_pos_invoice( item=item.name, rate=300, qty=1, do_not_submit=1, batch_no="TestBatch 01" ) + pos_inv1.append( + "payments", + {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300}, + ) pos_inv1.save() pos_inv1.submit() diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py index 904d8e83b9c..e0d37436be5 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py @@ -28,14 +28,17 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): pos_inv = create_pos_invoice(rate=300, do_not_submit=1) pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300}) + pos_inv.save() pos_inv.submit() pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.save() pos_inv2.submit() pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1) pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}) + pos_inv3.save() pos_inv3.submit() consolidate_pos_invoices() @@ -61,14 +64,17 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): pos_inv = create_pos_invoice(rate=300, do_not_submit=1) pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300}) + pos_inv.save() pos_inv.submit() pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.save() pos_inv2.submit() pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1) pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}) + pos_inv3.save() pos_inv3.submit() pos_inv_cn = make_sales_return(pos_inv.name) @@ -122,6 +128,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): }, ) inv.insert() + inv.payments[0].amount = inv.grand_total + inv.save() inv.submit() inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True) @@ -138,6 +146,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): }, ) inv2.insert() + inv2.payments[0].amount = inv.grand_total + inv2.save() inv2.submit() consolidate_pos_invoices() @@ -272,7 +282,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): inv2.submit() inv3 = create_pos_invoice(qty=3, rate=600, do_not_save=True) - inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000}) + inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1800}) inv3.insert() inv3.submit() @@ -280,8 +290,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): inv.load_from_db() consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice) - self.assertEqual(consolidated_invoice.outstanding_amount, 800) - self.assertNotEqual(consolidated_invoice.status, "Paid") + self.assertNotEqual(consolidated_invoice.outstanding_amount, 800) + self.assertEqual(consolidated_invoice.status, "Paid") finally: frappe.set_user("Administrator") @@ -416,6 +426,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): do_not_submit=1, ) pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) + pos_inv.save() pos_inv.submit() pos_inv_cn = make_sales_return(pos_inv.name) @@ -430,6 +441,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): do_not_submit=1, ) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) + pos_inv2.save() pos_inv2.submit() consolidate_pos_invoices()