diff --git a/posnext/overrides/pos_invoice.py b/posnext/overrides/pos_invoice.py index b9da847..8b69b5e 100644 --- a/posnext/overrides/pos_invoice.py +++ b/posnext/overrides/pos_invoice.py @@ -2,6 +2,7 @@ from erpnext.accounts.doctype.pos_invoice.pos_invoice import ( get_bin_qty, get_bundle_availability, + get_pos_reserved_qty, ) @@ -10,14 +11,13 @@ def get_stock_availability(item_code: str, warehouse: str) -> tuple: if frappe.db.get_value("Item", item_code, "is_stock_item"): is_stock_item = True bin_qty = get_bin_qty(item_code, warehouse) - # pos_sales_qty = get_pos_reserved_qty(item_code, warehouse) + pos_sales_qty = get_pos_reserved_qty(item_code, warehouse) - return bin_qty, is_stock_item + return bin_qty - pos_sales_qty, is_stock_item else: is_stock_item = True if frappe.db.exists("Product Bundle", {"name": item_code, "disabled": 0}): return get_bundle_availability(item_code, warehouse), is_stock_item else: is_stock_item = False - # Is a service item or non_stock item return 0, is_stock_item diff --git a/posnext/posnext/page/posnext/point_of_sale.py b/posnext/posnext/page/posnext/point_of_sale.py index 825b66c..a5e0079 100644 --- a/posnext/posnext/page/posnext/point_of_sale.py +++ b/posnext/posnext/page/posnext/point_of_sale.py @@ -132,6 +132,7 @@ def get_items( "custom_show_alternative_item_for_pos_search", "custom_show_logical_rack", "custom_skip_stock_transaction_validation", + "selling_price_list", ], as_dict=False, ) @@ -139,10 +140,10 @@ def get_items( if not result: frappe.throw(f"POS Profile {pos_profile} not found") - # Ensure we have exactly 6 values - if not isinstance(result, (tuple, list)) or len(result) != 6: + # Ensure we have exactly 7 values + if not isinstance(result, (tuple, list)) or len(result) != 7: frappe.throw( - f"Invalid POS Profile configuration. Expected 6 fields but got {len(result) if isinstance(result, (tuple, list)) else 'invalid response'}" + f"Invalid POS Profile configuration. Expected 7 fields but got {len(result) if isinstance(result, (tuple, list)) else 'invalid response'}" ) ( @@ -152,6 +153,7 @@ def get_items( custom_show_alternative_item_for_pos_search, custom_show_logical_rack, custom_skip_stock_transaction_validation, + pos_profile_price_list, ) = result result = [] @@ -293,6 +295,23 @@ def get_items( limit=1, ) + if ( + not item_price + and price_list != pos_profile_price_list + and pos_profile_price_list + ): + item_price = frappe.get_all( + "Item Price", + fields=["price_list_rate", "currency", "uom", "batch_no"], + filters={ + "price_list": pos_profile_price_list, + "item_code": item.item_code, + "selling": True, + }, + order_by="creation desc", + limit=1, + ) + if not item_price: result.append(item) diff --git a/posnext/public/js/pos_controller.js b/posnext/public/js/pos_controller.js index a6c9a4c..3d3d239 100644 --- a/posnext/public/js/pos_controller.js +++ b/posnext/public/js/pos_controller.js @@ -391,6 +391,15 @@ posnext.PointOfSale.Controller = class { this.customer_details = details; // will add/remove LP payment method this.payment.render_loyalty_points_payment_mode(); + // refresh item prices using the customer's own price list, bypassing + // doc.selling_price_list which may not be updated yet at this point + if (details && details.default_price_list) { + this.item_selector.filter_items({ + price_list: details.default_price_list, + }); + } else { + this.item_selector.filter_items(); + } }, }, }); diff --git a/posnext/public/js/pos_item_cart.js b/posnext/public/js/pos_item_cart.js index 7d1bc23..53644b1 100644 --- a/posnext/public/js/pos_item_cart.js +++ b/posnext/public/js/pos_item_cart.js @@ -806,6 +806,7 @@ posnext.PointOfSale.ItemCart = class { "mobile_no", "image", "loyalty_program", + "default_price_list", ]) .then(({ message }) => { const { loyalty_program } = message; @@ -1031,6 +1032,20 @@ posnext.PointOfSale.ItemCart = class { } } + refresh_totals_display() { + const frm = this.events.get_frm(); + if (!frm || !frm.doc) return; + const items = frm.doc.items || []; + this.render_net_total(items); + this.render_total_item_qty(items); + let grand_total = cint(frappe.sys_defaults.disable_rounded_total) + ? frm.doc.grand_total + : frm.doc.rounded_total; + if (!items.length && Math.abs(grand_total) != 0.005) grand_total = 0.0; + this.render_grand_total(grand_total); + this.render_taxes(frm.doc.taxes); + } + update_totals_section(frm) { if (!frm) frm = this.events.get_frm(); frm.cscript.calculate_taxes_and_totals(); @@ -1174,6 +1189,7 @@ posnext.PointOfSale.ItemCart = class { this.highlight_checkout_btn(true); this.update_empty_cart_section(no_of_cart_items); + this.refresh_totals_display(); } render_cart_item(item_data, $item_to_update) { diff --git a/posnext/public/js/pos_item_details.js b/posnext/public/js/pos_item_details.js index 433dcd9..477bddf 100644 --- a/posnext/public/js/pos_item_details.js +++ b/posnext/public/js/pos_item_details.js @@ -30,7 +30,7 @@ posnext.PointOfSale.ItemDetails = class { init_child_components() { this.$component.html( `
-
${__("Item Detailss")}
+
${__("Item Details")}
@@ -40,17 +40,19 @@ posnext.PointOfSale.ItemDetails = class {
-
+
-
`, +
+
`, ); this.$item_name = this.$component.find(".item-name"); + this.$item_code = this.$component.find(".item-code"); this.$item_description = this.$component.find(".item-desc"); this.$item_price = this.$component.find(".item-price"); this.$item_image = this.$component.find(".item-image"); @@ -121,7 +123,7 @@ posnext.PointOfSale.ItemDetails = class { } render_dom(item) { - let { item_name, description, image, price_list_rate } = item; + let { item_name, item_code, description, image, price_list_rate } = item; function get_description_html() { if (description) { @@ -135,6 +137,7 @@ posnext.PointOfSale.ItemDetails = class { } this.$item_name.html(item_name); + this.$item_code.html(item_code); this.$item_description.html(get_description_html()); this.$item_price.html(format_currency(price_list_rate, this.currency)); if (!this.hide_images && image) { diff --git a/posnext/public/js/pos_item_selector.js b/posnext/public/js/pos_item_selector.js index d05ec88..722da02 100644 --- a/posnext/public/js/pos_item_selector.js +++ b/posnext/public/js/pos_item_selector.js @@ -337,9 +337,15 @@ posnext.PointOfSale.ItemSelector = class { }); } - get_items({ start = 0, page_length = 40, search_term = "" }) { + get_items({ + start = 0, + page_length = 40, + search_term = "", + price_list: price_list_override = null, + }) { const doc = this.events.get_frm().doc; - const price_list = (doc && doc.selling_price_list) || this.price_list; + const price_list = + price_list_override || (doc && doc.selling_price_list) || this.price_list; let { item_group, pos_profile } = this; !item_group && (item_group = this.parent_item_group); @@ -716,7 +722,7 @@ posnext.PointOfSale.ItemSelector = class { df: { label: __("Search"), fieldtype: "Data", - placeholder: __("Search by serial number or barcode"), + placeholder: __("Search by item code, name, barcode, or serial number"), }, parent: this.$component.find(".search-field"), render_input: true, @@ -952,7 +958,7 @@ posnext.PointOfSale.ItemSelector = class { }); } - filter_items({ search_term = "" } = {}) { + filter_items({ search_term = "", price_list = null } = {}) { if (search_term) { search_term = search_term.toLowerCase(); @@ -969,7 +975,7 @@ posnext.PointOfSale.ItemSelector = class { } } - this.get_items({ search_term }).then(({ message }) => { + this.get_items({ search_term, price_list }).then(({ message }) => { const { items, serial_no, batch_no, barcode } = message; if (search_term && !barcode) { this.search_index[search_term] = items; diff --git a/posnext/public/js/pos_payment.js b/posnext/public/js/pos_payment.js index c883d1e..d4ba387 100644 --- a/posnext/public/js/pos_payment.js +++ b/posnext/public/js/pos_payment.js @@ -9,6 +9,8 @@ posnext.PointOfSale.Payment = class { this.custom_edit_rate = settings.custom_edit_rate_and_uom; this.custom_show_credit_sales = settings.custom_show_credit_sales; this.default_payment = settings.default_payment; + this.disable_grand_total_to_default_mop = + settings.disable_grand_total_to_default_mop; this.current_payments = []; this.enable_coupon_code = settings.enable_coupon_code; @@ -593,6 +595,7 @@ posnext.PointOfSale.Payment = class { } focus_on_default_mop() { + if (this.disable_grand_total_to_default_mop) return; const doc = this.events.get_frm().doc; const payments = doc.payments; payments.forEach((p) => {