fix: disable partial payment in pos (backport #45752) (#45945)

* fix: disable partial payment in pos (#45752)

* fix: disable partial payment in pos

* test: disable partial payment

* test: removed print statement

* test: using save method to auto calculate paid_amount

* test: paid_amount calculation using save method

* test: added save method to calculate paid_amount

* test: outstanding amount

* test: added test for partial payments in pos invoice

* fix: custom validation error for partial payment

* test: using partial payment validation

* fix: validate only on submit

(cherry picked from commit d94802067b)

# Conflicts:
#	erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
#	erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py

* chore: resolve conflict

* chore: resolve conflict

* chore: resolve linter issue

* test: fixed failing test

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
This commit is contained in:
mergify[bot]
2025-02-17 17:01:34 +05:30
committed by GitHub
parent dd34bbe570
commit 38edc46c46
4 changed files with 115 additions and 11 deletions

View File

@@ -39,10 +39,12 @@ class TestPOSClosingEntry(unittest.TestCase):
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit() pos_inv1.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
pcv_doc = make_closing_entry_from_opening(opening_entry) 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 = 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv.save()
pos_inv.submit() pos_inv.submit()
pcv_doc = make_closing_entry_from_opening(opening_entry) 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 = create_pos_invoice(rate=3500, do_not_submit=1)
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit() pos_inv1.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
# make return entry of pos_inv2 # 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 = create_pos_invoice(rate=3500, do_not_submit=1)
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit() pos_inv1.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
pcv_doc = make_closing_entry_from_opening(opening_entry) 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) 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 = 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
pos_inv1.save()
pos_inv1.submit() pos_inv1.submit()
# if in between a mandatory accounting dimension is added to the POS Profile then # 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) opening_entry = create_opening_entry(pos_profile, test_user.name)
pos_inv = create_pos_invoice( 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( 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) batch_qty_with_pos = get_batch_qty(batch_no, "_Test Warehouse - _TC", item_code)
self.assertEqual(batch_qty_with_pos, 0.0) self.assertEqual(batch_qty_with_pos, 0.0)

View File

@@ -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 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
class PartialPaymentValidationError(frappe.ValidationError):
pass
class POSInvoice(SalesInvoice): class POSInvoice(SalesInvoice):
# begin: auto-generated types # begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block. # 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_payment_amount()
self.validate_loyalty_transaction() self.validate_loyalty_transaction()
self.validate_company_with_pos_company() self.validate_company_with_pos_company()
self.validate_full_payment()
if self.coupon_code: if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import validate_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: if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
validate_loyalty_points(self, 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): def set_status(self, update=False, status=None, update_modified=True):
if self.is_new(): if self.is_new():
if self.get("amended_from"): if self.get("amended_from"):

View File

@@ -7,7 +7,7 @@ import unittest
import frappe import frappe
from frappe import _ 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.pos_profile.test_pos_profile import make_pos_profile
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
@@ -313,7 +313,7 @@ class TestPOSInvoice(unittest.TestCase):
) )
pos.append( 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() pos.insert()
@@ -324,6 +324,11 @@ class TestPOSInvoice(unittest.TestCase):
# partial return 1 # partial return 1
pos_return1.get("items")[0].qty = -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.submit()
pos_return1.reload() pos_return1.reload()
@@ -338,6 +343,11 @@ class TestPOSInvoice(unittest.TestCase):
# partial return 2 # partial return 2
pos_return2 = make_sales_return(pos.name) 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() pos_return2.submit()
self.assertEqual(pos_return2.get("items")[0].qty, -1) self.assertEqual(pos_return2.get("items")[0].qty, -1)
@@ -373,6 +383,15 @@ class TestPOSInvoice(unittest.TestCase):
inv.payments = [] inv.payments = []
self.assertRaises(frappe.ValidationError, inv.insert) 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): def test_serialized_item_transaction(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item 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" "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( lpe = frappe.get_doc(
"Loyalty Point Entry", "Loyalty Point Entry",
@@ -607,7 +632,13 @@ class TestPOSInvoice(unittest.TestCase):
) )
# add 10 loyalty points # 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( before_lp_details = get_loyalty_program_details_with_points(
"Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty" "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() 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 = 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 270})
pos_inv.save()
pos_inv.submit() pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
consolidate_pos_invoices() consolidate_pos_invoices()
@@ -676,6 +709,7 @@ class TestPOSInvoice(unittest.TestCase):
"included_in_print_rate": 1, "included_in_print_rate": 1,
}, },
) )
pos_inv.save()
pos_inv.submit() pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=300, qty=2, do_not_submit=1) 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, "included_in_print_rate": 1,
}, },
) )
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
consolidate_pos_invoices() consolidate_pos_invoices()
@@ -744,6 +779,7 @@ class TestPOSInvoice(unittest.TestCase):
"included_in_print_rate": 1, "included_in_print_rate": 1,
}, },
) )
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
consolidate_pos_invoices() consolidate_pos_invoices()
@@ -774,7 +810,10 @@ class TestPOSInvoice(unittest.TestCase):
# POS Invoice 1, for the batch without bundle # 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 = 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.items[0].batch_no = batch_no
pos_inv1.save() pos_inv1.save()
pos_inv1.submit() pos_inv1.submit()
@@ -790,8 +829,14 @@ class TestPOSInvoice(unittest.TestCase):
# POS Invoice 2, for the batch with bundle # POS Invoice 2, for the batch with bundle
pos_inv2 = create_pos_invoice( 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() pos_inv2.reload()
self.assertTrue(pos_inv2.items[0].serial_and_batch_bundle) self.assertTrue(pos_inv2.items[0].serial_and_batch_bundle)
@@ -826,6 +871,10 @@ class TestPOSInvoice(unittest.TestCase):
pos_inv1 = create_pos_invoice( pos_inv1 = create_pos_invoice(
item=item.name, rate=300, qty=1, do_not_submit=1, batch_no="TestBatch 01" 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.save()
pos_inv1.submit() pos_inv1.submit()

View File

@@ -28,14 +28,17 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
pos_inv = create_pos_invoice(rate=300, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
pos_inv.save()
pos_inv.submit() pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
pos_inv3.save()
pos_inv3.submit() pos_inv3.submit()
consolidate_pos_invoices() consolidate_pos_invoices()
@@ -61,14 +64,17 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
pos_inv = create_pos_invoice(rate=300, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
pos_inv.save()
pos_inv.submit() pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1) 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.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
pos_inv3.save()
pos_inv3.submit() pos_inv3.submit()
pos_inv_cn = make_sales_return(pos_inv.name) pos_inv_cn = make_sales_return(pos_inv.name)
@@ -122,6 +128,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
}, },
) )
inv.insert() inv.insert()
inv.payments[0].amount = inv.grand_total
inv.save()
inv.submit() inv.submit()
inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True) inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True)
@@ -138,6 +146,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
}, },
) )
inv2.insert() inv2.insert()
inv2.payments[0].amount = inv.grand_total
inv2.save()
inv2.submit() inv2.submit()
consolidate_pos_invoices() consolidate_pos_invoices()
@@ -272,7 +282,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
inv2.submit() inv2.submit()
inv3 = create_pos_invoice(qty=3, rate=600, do_not_save=True) 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.insert()
inv3.submit() inv3.submit()
@@ -280,8 +290,8 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
inv.load_from_db() inv.load_from_db()
consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice) consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
self.assertEqual(consolidated_invoice.outstanding_amount, 800) self.assertNotEqual(consolidated_invoice.outstanding_amount, 800)
self.assertNotEqual(consolidated_invoice.status, "Paid") self.assertEqual(consolidated_invoice.status, "Paid")
finally: finally:
frappe.set_user("Administrator") frappe.set_user("Administrator")
@@ -416,6 +426,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
do_not_submit=1, do_not_submit=1,
) )
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
pos_inv.save()
pos_inv.submit() pos_inv.submit()
pos_inv_cn = make_sales_return(pos_inv.name) pos_inv_cn = make_sales_return(pos_inv.name)
@@ -430,6 +441,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
do_not_submit=1, do_not_submit=1,
) )
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
pos_inv2.save()
pos_inv2.submit() pos_inv2.submit()
consolidate_pos_invoices() consolidate_pos_invoices()