mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-01 11:19:09 +00:00
Merge pull request #51613 from diptanilsaha/pos_item_selector_ui_ux
This commit is contained in:
@@ -117,6 +117,42 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
&.item-loading {
|
||||||
|
position: relative;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.item-loading::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
#f3f3f3 0px,
|
||||||
|
#f3f3f3 160px,
|
||||||
|
#e9ecef 160px,
|
||||||
|
#e9ecef 320px
|
||||||
|
);
|
||||||
|
animation: skeletonMove 1.1s linear infinite;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes skeletonMove {
|
||||||
|
from {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-position: 320px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.items-not-found {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&.show-item-image {
|
&.show-item-image {
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
gap: var(--margin-lg);
|
gap: var(--margin-lg);
|
||||||
|
|||||||
@@ -122,11 +122,13 @@ def filter_result_items(result, pos_profile):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_parent_item_group():
|
def get_parent_item_group(pos_profile):
|
||||||
# Using get_all to ignore user permission
|
item_groups = get_item_groups(pos_profile)
|
||||||
item_group = frappe.get_all("Item Group", {"lft": 1, "is_group": 1}, pluck="name")
|
|
||||||
if item_group:
|
if not item_groups:
|
||||||
return item_group[0]
|
item_groups = frappe.get_all("Item Group", {"lft": 1, "is_group": 1}, pluck="name")
|
||||||
|
|
||||||
|
return item_groups[0] if item_groups else None
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
this.events = events;
|
this.events = events;
|
||||||
this.pos_profile = pos_profile;
|
this.pos_profile = pos_profile;
|
||||||
this.hide_images = settings.hide_images;
|
this.hide_images = settings.hide_images;
|
||||||
|
this.item_display_class = this.hide_images ? "hide-item-image" : "show-item-image";
|
||||||
this.auto_add_item = settings.auto_add_item_to_cart;
|
this.auto_add_item = settings.auto_add_item_to_cart;
|
||||||
|
|
||||||
|
this.item_ready_group = this.get_parent_item_group();
|
||||||
this.inti_component();
|
this.inti_component();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,28 +37,36 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
this.$component = this.wrapper.find(".items-selector");
|
this.$component = this.wrapper.find(".items-selector");
|
||||||
this.$items_container = this.$component.find(".items-container");
|
this.$items_container = this.$component.find(".items-container");
|
||||||
|
|
||||||
const show_hide_images = this.hide_images ? "hide-item-image" : "show-item-image";
|
this.$items_container.addClass(this.item_display_class);
|
||||||
this.$items_container.addClass(show_hide_images);
|
}
|
||||||
|
|
||||||
|
async get_parent_item_group() {
|
||||||
|
const r = await frappe.call({
|
||||||
|
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_parent_item_group",
|
||||||
|
args: {
|
||||||
|
pos_profile: this.pos_profile,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (r.message) this.item_group = this.parent_item_group = r.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
async load_items_data() {
|
async load_items_data() {
|
||||||
if (!this.item_group) {
|
await this.item_ready_group;
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_parent_item_group",
|
this.start_item_loading_animation();
|
||||||
async: false,
|
|
||||||
callback: (r) => {
|
|
||||||
if (r.message) this.parent_item_group = r.message;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!this.price_list) {
|
if (!this.price_list) {
|
||||||
const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
|
const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
|
||||||
this.price_list = res.message.selling_price_list;
|
this.price_list = res.message.selling_price_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get_items({}).then(({ message }) => {
|
this.get_items({})
|
||||||
this.render_item_list(message.items);
|
.then(({ message }) => {
|
||||||
});
|
this.render_item_list(message.items);
|
||||||
|
})
|
||||||
|
.always(() => {
|
||||||
|
this.stop_item_loading_animation();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get_items({ start = 0, page_length = 40, search_term = "" }) {
|
get_items({ start = 0, page_length = 40, search_term = "" }) {
|
||||||
@@ -64,8 +74,6 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
const price_list = (doc && doc.selling_price_list) || this.price_list;
|
const price_list = (doc && doc.selling_price_list) || this.price_list;
|
||||||
let { item_group, pos_profile } = this;
|
let { item_group, pos_profile } = this;
|
||||||
|
|
||||||
!item_group && (item_group = this.parent_item_group);
|
|
||||||
|
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
|
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
|
||||||
freeze: true,
|
freeze: true,
|
||||||
@@ -76,16 +84,32 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
render_item_list(items) {
|
render_item_list(items) {
|
||||||
this.$items_container.html("");
|
this.$items_container.html("");
|
||||||
|
|
||||||
|
if (!items?.length) {
|
||||||
|
this.set_items_not_found_banner();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$items_container.hasClass("items-not-found")) {
|
||||||
|
this.$items_container.removeClass("items-not-found");
|
||||||
|
this.$items_container.addClass(this.item_display_class);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hide_images) {
|
if (this.hide_images) {
|
||||||
this.$items_container.append(this.render_item_list_column_header());
|
this.$items_container.append(this.render_item_list_column_header());
|
||||||
}
|
}
|
||||||
|
|
||||||
items.forEach((item) => {
|
items?.forEach((item) => {
|
||||||
const item_html = this.get_item_html(item);
|
const item_html = this.get_item_html(item);
|
||||||
this.$items_container.append(item_html);
|
this.$items_container.append(item_html);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_items_not_found_banner() {
|
||||||
|
this.$items_container.removeClass(this.item_display_class);
|
||||||
|
this.$items_container.addClass("items-not-found");
|
||||||
|
this.$items_container.html(__("Items not found."));
|
||||||
|
}
|
||||||
|
|
||||||
render_item_list_column_header() {
|
render_item_list_column_header() {
|
||||||
return `<div class="list-column">
|
return `<div class="list-column">
|
||||||
<div class="column-name">Name</div>
|
<div class="column-name">Name</div>
|
||||||
@@ -189,17 +213,18 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Item Group",
|
options: "Item Group",
|
||||||
placeholder: __("Select item group"),
|
placeholder: __("Select item group"),
|
||||||
|
only_select: true,
|
||||||
onchange: function () {
|
onchange: function () {
|
||||||
me.item_group = this.value;
|
me.item_group = this.value;
|
||||||
!me.item_group && (me.item_group = me.parent_item_group);
|
!me.item_group && (me.item_group = me.parent_item_group);
|
||||||
me.filter_items();
|
me.filter_items();
|
||||||
|
me.set_item_selector_filter_label(this.value);
|
||||||
},
|
},
|
||||||
get_query: function () {
|
get_query: function () {
|
||||||
const doc = me.events.get_frm().doc;
|
|
||||||
return {
|
return {
|
||||||
query: "erpnext.selling.page.point_of_sale.point_of_sale.item_group_query",
|
query: "erpnext.selling.page.point_of_sale.point_of_sale.item_group_query",
|
||||||
filters: {
|
filters: {
|
||||||
pos_profile: doc ? doc.pos_profile : "",
|
pos_profile: me.pos_profile,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -210,9 +235,22 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
this.search_field.toggle_label(false);
|
this.search_field.toggle_label(false);
|
||||||
this.item_group_field.toggle_label(false);
|
this.item_group_field.toggle_label(false);
|
||||||
|
|
||||||
|
$(this.item_group_field.awesomplete.ul).css("min-width", "unset");
|
||||||
|
|
||||||
|
this.hide_open_link_btn();
|
||||||
this.attach_clear_btn();
|
this.attach_clear_btn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_item_selector_filter_label(value) {
|
||||||
|
const $filter_label = this.$component.find(".label");
|
||||||
|
|
||||||
|
$filter_label.html(value ? __(value) : __("All Items"));
|
||||||
|
}
|
||||||
|
|
||||||
|
hide_open_link_btn() {
|
||||||
|
$(this.item_group_field.$wrapper.find(".btn-open")).css("display", "none");
|
||||||
|
}
|
||||||
|
|
||||||
attach_clear_btn() {
|
attach_clear_btn() {
|
||||||
this.search_field.$wrapper.find(".control-input").append(
|
this.search_field.$wrapper.find(".control-input").append(
|
||||||
`<span class="link-btn">
|
`<span class="link-btn">
|
||||||
@@ -222,12 +260,24 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
</span>`
|
</span>`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.item_group_field.$wrapper.find(".link-btn").append(
|
||||||
|
`<a class="btn-clear" tabindex="-1" style="display: inline-block;" title="${__("Clear Link")}">
|
||||||
|
${frappe.utils.icon("close", "xs", "es-icon")}
|
||||||
|
</a>`
|
||||||
|
);
|
||||||
|
|
||||||
this.$clear_search_btn = this.search_field.$wrapper.find(".link-btn");
|
this.$clear_search_btn = this.search_field.$wrapper.find(".link-btn");
|
||||||
|
this.$clear_item_group_btn = this.item_group_field.$wrapper.find(".btn-clear");
|
||||||
|
|
||||||
this.$clear_search_btn.on("click", "a", () => {
|
this.$clear_search_btn.on("click", "a", () => {
|
||||||
this.set_search_value("");
|
this.set_search_value("");
|
||||||
this.search_field.set_focus();
|
this.search_field.set_focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.$clear_item_group_btn.on("click", () => {
|
||||||
|
$(this.item_group_field.$input[0]).val("").trigger("input");
|
||||||
|
this.item_group_field.set_focus();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
set_search_value(value) {
|
set_search_value(value) {
|
||||||
@@ -359,6 +409,8 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filter_items({ search_term = "" } = {}) {
|
filter_items({ search_term = "" } = {}) {
|
||||||
|
this.start_item_loading_animation();
|
||||||
|
|
||||||
const selling_price_list = this.events.get_frm().doc.selling_price_list;
|
const selling_price_list = this.events.get_frm().doc.selling_price_list;
|
||||||
|
|
||||||
if (search_term) {
|
if (search_term) {
|
||||||
@@ -379,19 +431,31 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get_items({ search_term }).then(({ message }) => {
|
this.get_items({ search_term })
|
||||||
// eslint-disable-next-line no-unused-vars
|
.then(({ message }) => {
|
||||||
const { items, serial_no, batch_no, barcode } = message;
|
// eslint-disable-next-line no-unused-vars
|
||||||
if (search_term && !barcode) {
|
const { items, serial_no, batch_no, barcode } = message;
|
||||||
this.search_index[selling_price_list][search_term] = items;
|
if (search_term && !barcode) {
|
||||||
}
|
this.search_index[selling_price_list][search_term] = items;
|
||||||
this.items = items;
|
}
|
||||||
this.render_item_list(items);
|
this.items = items;
|
||||||
this.auto_add_item &&
|
this.render_item_list(items);
|
||||||
this.search_field.$input[0].value &&
|
this.auto_add_item &&
|
||||||
this.items.length == 1 &&
|
this.search_field.$input[0].value &&
|
||||||
this.add_filtered_item_to_cart();
|
this.items.length == 1 &&
|
||||||
});
|
this.add_filtered_item_to_cart();
|
||||||
|
})
|
||||||
|
.always(() => {
|
||||||
|
this.stop_item_loading_animation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
start_item_loading_animation() {
|
||||||
|
this.$items_container.addClass("is-loading");
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_item_loading_animation() {
|
||||||
|
this.$items_container.removeClass("is-loading");
|
||||||
}
|
}
|
||||||
|
|
||||||
add_filtered_item_to_cart() {
|
add_filtered_item_to_cart() {
|
||||||
|
|||||||
Reference in New Issue
Block a user