fix(pos): validations & minor ui issues (#25351)
* fix: pos print format * fix: stock validation on pos invoice creation
This commit is contained in:
		| @@ -16,28 +16,8 @@ class POSClosingEntry(StatusUpdater): | ||||
| 		if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open": | ||||
| 			frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry")) | ||||
|  | ||||
| 		self.validate_pos_closing() | ||||
| 		self.validate_pos_invoices() | ||||
|  | ||||
| 	def validate_pos_closing(self): | ||||
| 		user = frappe.db.sql(""" | ||||
| 			SELECT name FROM `tabPOS Closing Entry` | ||||
| 			WHERE | ||||
| 				user = %(user)s AND docstatus = 1 AND pos_profile = %(profile)s AND | ||||
| 				(period_start_date between %(start)s and %(end)s OR period_end_date between %(start)s and %(end)s) | ||||
| 			""", { | ||||
| 				'user': self.user, | ||||
| 				'profile': self.pos_profile, | ||||
| 				'start': self.period_start_date, | ||||
| 				'end': self.period_end_date | ||||
| 			}) | ||||
|  | ||||
| 		if user: | ||||
| 			bold_already_exists = frappe.bold(_("already exists")) | ||||
| 			bold_user = frappe.bold(self.user) | ||||
| 			frappe.throw(_("POS Closing Entry {} against {} between selected period") | ||||
| 				.format(bold_already_exists, bold_user), title=_("Invalid Period")) | ||||
|  | ||||
| 	def validate_pos_invoices(self): | ||||
| 		invalid_rows = [] | ||||
| 		for d in self.pos_transactions: | ||||
|   | ||||
| @@ -96,30 +96,45 @@ class POSInvoice(SalesInvoice): | ||||
| 				if paid_amt and pay.amount != paid_amt: | ||||
| 					return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment)) | ||||
|  | ||||
| 	def validate_pos_reserved_serial_nos(self, item): | ||||
| 		serial_nos = get_serial_nos(item.serial_no) | ||||
| 		filters = {"item_code": item.item_code, "warehouse": item.warehouse} | ||||
| 		if item.batch_no: | ||||
| 			filters["batch_no"] = item.batch_no | ||||
|  | ||||
| 		reserved_serial_nos = get_pos_reserved_serial_nos(filters) | ||||
| 		invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos] | ||||
|  | ||||
| 		bold_invalid_serial_nos = frappe.bold(', '.join(invalid_serial_nos)) | ||||
| 		if len(invalid_serial_nos) == 1: | ||||
| 			frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.") | ||||
| 						.format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable")) | ||||
| 		elif invalid_serial_nos: | ||||
| 			frappe.throw(_("Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.") | ||||
| 						.format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable")) | ||||
|  | ||||
| 	def validate_delivered_serial_nos(self, item): | ||||
| 		serial_nos = get_serial_nos(item.serial_no) | ||||
| 		delivered_serial_nos = frappe.db.get_list('Serial No', { | ||||
| 			'item_code': item.item_code, | ||||
| 			'name': ['in', serial_nos], | ||||
| 			'sales_invoice': ['is', 'set'] | ||||
| 		}, pluck='name') | ||||
|  | ||||
| 		if delivered_serial_nos: | ||||
| 			bold_delivered_serial_nos = frappe.bold(', '.join(delivered_serial_nos)) | ||||
| 			frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another Sales Invoice. Please select valid serial no.") | ||||
| 						.format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable")) | ||||
|  | ||||
| 	def validate_stock_availablility(self): | ||||
| 		if self.is_return: | ||||
| 			return | ||||
|  | ||||
| 		allow_negative_stock = frappe.db.get_value('Stock Settings', None, 'allow_negative_stock') | ||||
| 		error_msg = [] | ||||
| 		allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock') | ||||
| 		for d in self.get('items'): | ||||
| 			msg = "" | ||||
| 			if d.serial_no: | ||||
| 				filters = { "item_code": d.item_code, "warehouse": d.warehouse } | ||||
| 				if d.batch_no: | ||||
| 					filters["batch_no"] = d.batch_no | ||||
| 				reserved_serial_nos = get_pos_reserved_serial_nos(filters) | ||||
| 				serial_nos = get_serial_nos(d.serial_no) | ||||
| 				invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos] | ||||
|  | ||||
| 				bold_invalid_serial_nos = frappe.bold(', '.join(invalid_serial_nos)) | ||||
| 				if len(invalid_serial_nos) == 1: | ||||
| 					msg = (_("Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.") | ||||
| 								.format(d.idx, bold_invalid_serial_nos)) | ||||
| 				elif invalid_serial_nos: | ||||
| 					msg = (_("Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.") | ||||
| 								.format(d.idx, bold_invalid_serial_nos)) | ||||
|  | ||||
| 				self.validate_pos_reserved_serial_nos(d) | ||||
| 				self.validate_delivered_serial_nos(d) | ||||
| 			else: | ||||
| 				if allow_negative_stock: | ||||
| 					return | ||||
| @@ -127,15 +142,11 @@ class POSInvoice(SalesInvoice): | ||||
| 				available_stock = get_stock_availability(d.item_code, d.warehouse) | ||||
| 				item_code, warehouse, qty = frappe.bold(d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty) | ||||
| 				if flt(available_stock) <= 0: | ||||
| 					msg = (_('Row #{}: Item Code: {} is not available under warehouse {}.').format(d.idx, item_code, warehouse)) | ||||
| 					frappe.throw(_('Row #{}: Item Code: {} is not available under warehouse {}.') | ||||
| 								.format(d.idx, item_code, warehouse), title=_("Item Unavailable")) | ||||
| 				elif flt(available_stock) < flt(d.qty): | ||||
| 					msg = (_('Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.') | ||||
| 								.format(d.idx, item_code, warehouse, qty)) | ||||
| 			if msg: | ||||
| 				error_msg.append(msg) | ||||
|  | ||||
| 		if error_msg: | ||||
| 			frappe.throw(error_msg, title=_("Item Unavailable"), as_list=True) | ||||
| 					frappe.throw(_('Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.') | ||||
| 								.format(d.idx, item_code, warehouse, available_stock), title=_("Item Unavailable")) | ||||
|  | ||||
| 	def validate_serialised_or_batched_item(self): | ||||
| 		error_msg = [] | ||||
| @@ -202,9 +213,8 @@ class POSInvoice(SalesInvoice): | ||||
| 		for d in self.get("items"): | ||||
| 			is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item") | ||||
| 			if not is_stock_item: | ||||
| 				frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice. ").format( | ||||
| 					d.idx, frappe.bold(d.item_code) | ||||
| 				), title=_("Invalid Item")) | ||||
| 				frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice. ") | ||||
| 					.format(d.idx, frappe.bold(d.item_code)), title=_("Invalid Item")) | ||||
|  | ||||
| 	def validate_mode_of_payment(self): | ||||
| 		if len(self.payments) == 0: | ||||
|   | ||||
| @@ -10,10 +10,12 @@ from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return | ||||
| from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry | ||||
| from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt | ||||
| from erpnext.stock.doctype.item.test_item import make_item | ||||
| from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice | ||||
|  | ||||
| class TestPOSInvoice(unittest.TestCase): | ||||
| 	@classmethod | ||||
| 	def setUpClass(cls): | ||||
| 		make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item", qty=800, basic_rate=100) | ||||
| 		frappe.db.sql("delete from `tabTax Rule`") | ||||
|  | ||||
| 	def tearDown(self): | ||||
| @@ -320,6 +322,34 @@ class TestPOSInvoice(unittest.TestCase): | ||||
|  | ||||
| 		self.assertRaises(frappe.ValidationError, pos2.insert) | ||||
|  | ||||
| 	def test_delivered_serialized_item_transaction(self): | ||||
| 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item | ||||
| 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos | ||||
|  | ||||
| 		se = make_serialized_item(company='_Test Company', | ||||
| 			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC') | ||||
|  | ||||
| 		serial_nos = get_serial_nos(se.get("items")[0].serial_no) | ||||
|  | ||||
| 		si = create_sales_invoice(company='_Test Company', debit_to='Debtors - _TC', | ||||
| 			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', | ||||
| 			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', | ||||
| 			item=se.get("items")[0].item_code, rate=1000, do_not_save=1) | ||||
|  | ||||
| 		si.get("items")[0].serial_no = serial_nos[0] | ||||
| 		si.insert() | ||||
| 		si.submit() | ||||
|  | ||||
| 		pos2 = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC', | ||||
| 			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC', | ||||
| 			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC', | ||||
| 			item=se.get("items")[0].item_code, rate=1000, do_not_save=1) | ||||
|  | ||||
| 		pos2.get("items")[0].serial_no = serial_nos[0] | ||||
| 		pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000}) | ||||
|  | ||||
| 		self.assertRaises(frappe.ValidationError, pos2.insert) | ||||
|  | ||||
| 	def test_loyalty_points(self): | ||||
| 		from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records | ||||
| 		from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points | ||||
|   | ||||
| @@ -105,7 +105,7 @@ erpnext.PointOfSale.PastOrderList = class { | ||||
| 						<svg class="mr-2" width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"> | ||||
| 							<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/> | ||||
| 						</svg> | ||||
| 						${invoice.customer} | ||||
| 						${frappe.ellipsis(invoice.customer, 20)} | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="invoice-total-status"> | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,4 +1,5 @@ | ||||
| { | ||||
|  "absolute_value": 0, | ||||
|  "align_labels_right": 0, | ||||
|  "creation": "2011-12-21 11:08:55", | ||||
|  "custom_format": 1, | ||||
| @@ -6,10 +7,10 @@ | ||||
|  "doc_type": "POS Invoice", | ||||
|  "docstatus": 0, | ||||
|  "doctype": "Print Format", | ||||
|  "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Tahoma, sans-serif;\n\t\tline-height: 150%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n{% if letter_head %}\n    {{ letter_head }}\n{% endif %}\n\n<p class=\"text-center\" style=\"margin-bottom: 1rem\">\n\t{{ doc.company }}<br>\n\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"SR.No\") }}:</b><br>\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t  {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t    {% if '%' in row.description %}\n\t\t\t\t\t    {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t    {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t  {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.change_amount -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>", | ||||
|  "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tline-height: 150%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n{% if letter_head %}\n    {{ letter_head }}\n{% endif %}\n\n<p class=\"text-center\" style=\"margin-bottom: 1rem\">\n\t{{ doc.company }}<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Cashier\") }}:</b> {{ doc.owner }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Time\") }}:</b> {{  doc.get_formatted(\"posting_time\") }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"SR.No\") }}:</b><br>\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t  {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t    {% if '%' in row.description %}\n\t\t\t\t\t    {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t    {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t  {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- for row in doc.payments -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t    {{ row.mode_of_payment }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t{%- endfor -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.change_amount -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>", | ||||
|  "idx": 1, | ||||
|  "line_breaks": 0, | ||||
|  "modified": "2020-04-29 16:45:58.942375", | ||||
|  "modified": "2021-04-15 15:23:28.867135", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Selling", | ||||
|  "name": "POS Invoice", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Saqib
					Saqib