Item search refactor
This commit is contained in:
parent
78c81d9c6c
commit
b73321c577
@ -431,7 +431,4 @@ erpnext.patches.v8_5.set_default_mode_of_payment
|
|||||||
erpnext.patches.v8_5.update_customer_group_in_POS_profile
|
erpnext.patches.v8_5.update_customer_group_in_POS_profile
|
||||||
erpnext.patches.v8_6.update_timesheet_company_from_PO
|
erpnext.patches.v8_6.update_timesheet_company_from_PO
|
||||||
erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
|
erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
|
||||||
<<<<<<< HEAD
|
|
||||||
erpnext.patches.v8_5.remove_project_type_property_setter
|
erpnext.patches.v8_5.remove_project_type_property_setter
|
||||||
=======
|
|
||||||
>>>>>>> Set write permission to sales manger for permlevel 1 in Quotation doctype
|
|
||||||
|
@ -39,6 +39,7 @@ class PointOfSale {
|
|||||||
this.make_cart();
|
this.make_cart();
|
||||||
this.make_items();
|
this.make_items();
|
||||||
this.bind_events();
|
this.bind_events();
|
||||||
|
this.disable_text_box_and_button();
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -72,6 +73,7 @@ class PointOfSale {
|
|||||||
|
|
||||||
make_cart() {
|
make_cart() {
|
||||||
this.cart = new POSCart({
|
this.cart = new POSCart({
|
||||||
|
frm: this.frm,
|
||||||
wrapper: this.wrapper.find('.cart-container'),
|
wrapper: this.wrapper.find('.cart-container'),
|
||||||
events: {
|
events: {
|
||||||
customer_change: (customer) => this.frm.set_value('customer', customer),
|
customer_change: (customer) => this.frm.set_value('customer', customer),
|
||||||
@ -93,6 +95,10 @@ class PointOfSale {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disable_text_box_and_button() {
|
||||||
|
$(this.wrapper).find('input, button').prop("disabled", !(this.frm.doc.docstatus===0));
|
||||||
|
}
|
||||||
|
|
||||||
make_items() {
|
make_items() {
|
||||||
this.items = new POSItems({
|
this.items = new POSItems({
|
||||||
wrapper: this.wrapper.find('.item-container'),
|
wrapper: this.wrapper.find('.item-container'),
|
||||||
@ -108,12 +114,18 @@ class PointOfSale {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
add_item_to_cart(item_code, qty = 1) {
|
add_item_to_cart(item_code, qty = 1, barcode) {
|
||||||
|
|
||||||
if(this.cart.exists(item_code)) {
|
if(this.cart.exists(item_code)) {
|
||||||
// increase qty by 1
|
// increase qty by 1
|
||||||
this.frm.doc.items.forEach((item) => {
|
this.frm.doc.items.forEach((item) => {
|
||||||
if (item.item_code === item_code) {
|
if (item.item_code === item_code) {
|
||||||
|
if (barcode) {
|
||||||
|
value = barcode['serial_no'] ?
|
||||||
|
item.serial_no + '\n' + barcode['serial_no'] : barcode['batch_no'];
|
||||||
|
frappe.model.set_value(item.doctype, item.name,
|
||||||
|
Object.keys(barcode)[0], final_qty);
|
||||||
|
} else {
|
||||||
const final_qty = item.qty + qty;
|
const final_qty = item.qty + qty;
|
||||||
frappe.model.set_value(item.doctype, item.name, 'qty', final_qty)
|
frappe.model.set_value(item.doctype, item.name, 'qty', final_qty)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -124,6 +136,7 @@ class PointOfSale {
|
|||||||
this.cart.add_item(item);
|
this.cart.add_item(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -200,7 +213,8 @@ class PointOfSale {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class POSCart {
|
class POSCart {
|
||||||
constructor({wrapper, events}) {
|
constructor({frm, wrapper, events}) {
|
||||||
|
this.frm = frm;
|
||||||
this.wrapper = wrapper;
|
this.wrapper = wrapper;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.make();
|
this.make();
|
||||||
@ -302,24 +316,8 @@ class POSCart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exists(item_code) {
|
|
||||||
let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
|
||||||
return $item.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlight_item(item_code) {
|
|
||||||
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
|
||||||
$item.addClass('highlight');
|
|
||||||
setTimeout(() => $item.removeClass('highlight'), 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
scroll_to_item(item_code) {
|
|
||||||
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
|
||||||
// const scrollTop = $item.offset().top - this.$cart_items.offset().top + this.$cart_items.scrollTop();
|
|
||||||
// this.$cart_items.animate({ scrollTop });
|
|
||||||
}
|
|
||||||
|
|
||||||
get_item_html(item) {
|
get_item_html(item) {
|
||||||
|
const rate = format_currency(item.rate, this.frm.doc.currency);
|
||||||
return `
|
return `
|
||||||
<div class="list-item" data-item-code="${item.item_code}">
|
<div class="list-item" data-item-code="${item.item_code}">
|
||||||
<div class="item-name list-item__content list-item__content--flex-2 ellipsis">
|
<div class="item-name list-item__content list-item__content--flex-2 ellipsis">
|
||||||
@ -332,7 +330,7 @@ class POSCart {
|
|||||||
${item.discount_percentage}%
|
${item.discount_percentage}%
|
||||||
</div>
|
</div>
|
||||||
<div class="rate list-item__content text-right">
|
<div class="rate list-item__content text-right">
|
||||||
${item.rate}
|
${rate}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -354,6 +352,23 @@ class POSCart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exists(item_code) {
|
||||||
|
let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||||
|
return $item.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight_item(item_code) {
|
||||||
|
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||||
|
$item.addClass('highlight');
|
||||||
|
setTimeout(() => $item.removeClass('highlight'), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
scroll_to_item(item_code) {
|
||||||
|
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||||
|
// const scrollTop = $item.offset().top - this.$cart_items.offset().top + this.$cart_items.scrollTop();
|
||||||
|
// this.$cart_items.animate({ scrollTop });
|
||||||
|
}
|
||||||
|
|
||||||
bind_events() {
|
bind_events() {
|
||||||
const events = this.events;
|
const events = this.events;
|
||||||
this.$cart_items.on('click',
|
this.$cart_items.on('click',
|
||||||
@ -381,6 +396,8 @@ class POSItems {
|
|||||||
this.wrapper = wrapper;
|
this.wrapper = wrapper;
|
||||||
this.pos_profile = pos_profile;
|
this.pos_profile = pos_profile;
|
||||||
this.items = {};
|
this.items = {};
|
||||||
|
this.currency = this.pos_profile.currency ||
|
||||||
|
frappe.defaults.get_default('currency');
|
||||||
|
|
||||||
this.make_dom();
|
this.make_dom();
|
||||||
this.make_fields();
|
this.make_fields();
|
||||||
@ -516,7 +533,8 @@ class POSItems {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_item_html(item) {
|
get_item_html(item) {
|
||||||
const { item_code, item_name, image: item_image, item_stock=0, item_price=0} = item;
|
const price_list_rate = format_currency(item.price_list_rate, this.currency)
|
||||||
|
const { item_code, item_name, item_image, item_stock=0} = item;
|
||||||
const item_title = item_name || item_code;
|
const item_title = item_name || item_code;
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
@ -542,7 +560,7 @@ class POSItems {
|
|||||||
${item_image ? `<img src="${item_image}" alt="${item_title}">` : '' }
|
${item_image ? `<img src="${item_image}" alt="${item_title}">` : '' }
|
||||||
</div>
|
</div>
|
||||||
<span class="price-info">
|
<span class="price-info">
|
||||||
${item_price}
|
${price_list_rate}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -552,38 +570,17 @@ class POSItems {
|
|||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_items(start = 0, page_length = 20) {
|
get_items(start = 10, page_length = 20) {
|
||||||
|
var me = this;
|
||||||
return new Promise(res => {
|
return new Promise(res => {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "frappe.desk.reportview.get",
|
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
|
||||||
type: "GET",
|
|
||||||
args: {
|
args: {
|
||||||
doctype: "Item",
|
'price_list': this.pos_profile.selling_price_list,
|
||||||
fields: [
|
'item': me.search_field.$input.value || ""
|
||||||
"`tabItem`.`name`",
|
|
||||||
"`tabItem`.`owner`",
|
|
||||||
"`tabItem`.`docstatus`",
|
|
||||||
"`tabItem`.`modified`",
|
|
||||||
"`tabItem`.`modified_by`",
|
|
||||||
"`tabItem`.`item_name`",
|
|
||||||
"`tabItem`.`item_code`",
|
|
||||||
"`tabItem`.`disabled`",
|
|
||||||
"`tabItem`.`item_group`",
|
|
||||||
"`tabItem`.`stock_uom`",
|
|
||||||
"`tabItem`.`image`",
|
|
||||||
"`tabItem`.`variant_of`",
|
|
||||||
"`tabItem`.`has_variants`",
|
|
||||||
"`tabItem`.`end_of_life`",
|
|
||||||
"`tabItem`.`total_projected_qty`"
|
|
||||||
],
|
|
||||||
filters: [['disabled', '=', '0']],
|
|
||||||
order_by: "`tabItem`.`modified` desc",
|
|
||||||
page_length: page_length,
|
|
||||||
start: start
|
|
||||||
}
|
}
|
||||||
}).then(r => {
|
}).then(r => {
|
||||||
const data = r.message;
|
const items = r.message;
|
||||||
const items = frappe.utils.dict(data.keys, data.values);
|
|
||||||
|
|
||||||
// convert to key, value
|
// convert to key, value
|
||||||
let items_dict = {};
|
let items_dict = {};
|
||||||
@ -687,8 +684,11 @@ class Payment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_primary_action() {
|
set_primary_action() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
this.dialog.set_primary_action(__("Submit"), function() {
|
this.dialog.set_primary_action(__("Submit"), function() {
|
||||||
// save form
|
this.frm.doc.savesubmit()
|
||||||
|
this.dialog.hide()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
erpnext/selling/page/point_of_sale/point_of_sale.py
Normal file
46
erpnext/selling/page/point_of_sale/point_of_sale.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe, json
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import nowdate
|
||||||
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
|
from frappe.core.doctype.communication.email import make
|
||||||
|
from erpnext.stock.get_item_details import get_pos_profile
|
||||||
|
from erpnext.accounts.party import get_party_account_currency
|
||||||
|
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_items(price_list, item=None):
|
||||||
|
condition = ""
|
||||||
|
order_by = ""
|
||||||
|
args = {"price_list": price_list}
|
||||||
|
|
||||||
|
if item:
|
||||||
|
# search serial no
|
||||||
|
item_code = frappe.db.sql("""select name as serial_no, item_code
|
||||||
|
from `tabSerial No` where name=%s""", (item), as_dict=1)
|
||||||
|
if item_code:
|
||||||
|
item_code[0]["name"] = item_code[0]["item_code"]
|
||||||
|
return item_code
|
||||||
|
|
||||||
|
# search barcode
|
||||||
|
item_code = frappe.db.sql("""select name, item_code from `tabItem`
|
||||||
|
where barcode=%s""",
|
||||||
|
(item), as_dict=1)
|
||||||
|
if item_code:
|
||||||
|
item_code[0]["barcode"] = item
|
||||||
|
return item_code
|
||||||
|
|
||||||
|
# locate function is used to sort by closest match from the beginning of the value
|
||||||
|
return frappe.db.sql("""select i.name as item_code, i.item_name, i.image as item_image,
|
||||||
|
item_det.price_list_rate, item_det.currency
|
||||||
|
from `tabItem` i LEFT JOIN
|
||||||
|
(select item_code, price_list_rate, currency from
|
||||||
|
`tabItem Price` where price_list=%(price_list)s) item_det
|
||||||
|
ON
|
||||||
|
(item_det.item_code=i.name or item_det.item_code=i.variant_of)
|
||||||
|
where
|
||||||
|
i.has_variants = 0 and (i.item_code like %(item_code)s or i.item_name like %(item_code)s)
|
||||||
|
limit 24""", {'item_code': '%%%s%%'%(frappe.db.escape(item)), 'price_list': price_list} , as_dict=1)
|
Loading…
Reference in New Issue
Block a user