') != -1) {
try {
item_data.description = $(item_data.description).text();
} catch (error) {
item_data.description = item_data.description.replace(/
/g, ' ').replace(/<\/div>/g, ' ').replace(/ +/g, ' ');
}
}
item_data.description = frappe.ellipsis(item_data.description, 45);
return `
${item_data.description}
`;
}
return ``;
}
function get_item_image_html() {
const { image, item_name } = item_data;
if (image) {
return `
`;
} else {
return `
${frappe.get_abbr(item_name)}
`;
}
}
}
handle_broken_image($img) {
const item_abbr = $($img).attr('alt');
$($img).parent().replaceWith(`
${item_abbr}
`);
}
scroll_to_item($item) {
if ($item.length === 0) return;
const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop();
this.$cart_items_wrapper.animate({ scrollTop });
}
update_selector_value_in_cart_item(selector, value, item) {
const $item_to_update = this.get_cart_item(item);
$item_to_update.attr(`data-${selector}`, escape(value));
}
toggle_checkout_btn(show_checkout) {
if (show_checkout) {
this.$totals_section.find('.checkout-btn').css('display', 'flex');
this.$totals_section.find('.edit-cart-btn').css('display', 'none');
} else {
this.$totals_section.find('.checkout-btn').css('display', 'none');
this.$totals_section.find('.edit-cart-btn').css('display', 'flex');
}
}
highlight_checkout_btn(toggle) {
if (toggle) {
this.$add_discount_elem.css('display', 'flex');
this.$cart_container.find('.checkout-btn').css({
'background-color': 'var(--blue-500)'
});
} else {
this.$add_discount_elem.css('display', 'none');
this.$cart_container.find('.checkout-btn').css({
'background-color': 'var(--blue-200)'
});
}
}
update_empty_cart_section(no_of_cart_items) {
const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper');
// if cart has items and no item is present
no_of_cart_items > 0 && $no_item_element && $no_item_element.remove() && this.$cart_header.css('display', 'flex');
no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder();
}
on_numpad_event($btn) {
const current_action = $btn.attr('data-button-value');
const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
const action_is_allowed = action_is_field_edit ? (
(current_action == 'rate' && this.allow_rate_change) ||
(current_action == 'discount_percentage' && this.allow_discount_change) ||
(current_action == 'qty')) : true;
const action_is_pressed_twice = this.prev_action === current_action;
const first_click_event = !this.prev_action;
const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
if (action_is_field_edit) {
if (!action_is_allowed) {
const label = current_action == 'rate' ? 'Rate'.bold() : 'Discount'.bold();
const message = __('Editing {0} is not allowed as per POS Profile settings', [label]);
frappe.show_alert({
indicator: 'red',
message: message
});
frappe.utils.play_sound("error");
return;
}
if (first_click_event || field_to_edit_changed) {
this.prev_action = current_action;
} else if (action_is_pressed_twice) {
this.prev_action = undefined;
}
this.numpad_value = '';
} else if (current_action === 'checkout') {
this.prev_action = undefined;
this.toggle_item_highlight();
this.events.numpad_event(undefined, current_action);
return;
} else if (current_action === 'remove') {
this.prev_action = undefined;
this.toggle_item_highlight();
this.events.numpad_event(undefined, current_action);
return;
} else {
this.numpad_value = current_action === 'delete' ? this.numpad_value.slice(0, -1) : this.numpad_value + current_action;
this.numpad_value = this.numpad_value || 0;
}
const first_click_event_is_not_field_edit = !action_is_field_edit && first_click_event;
if (first_click_event_is_not_field_edit) {
frappe.show_alert({
indicator: 'red',
message: __('Please select a field to edit from numpad')
});
frappe.utils.play_sound("error");
return;
}
if (flt(this.numpad_value) > 100 && this.prev_action === 'discount_percentage') {
frappe.show_alert({
message: __('Discount cannot be greater than 100%'),
indicator: 'orange'
});
frappe.utils.play_sound("error");
this.numpad_value = current_action;
}
this.highlight_numpad_btn($btn, current_action);
this.events.numpad_event(this.numpad_value, this.prev_action);
}
highlight_numpad_btn($btn, curr_action) {
const curr_action_is_highlighted = $btn.hasClass('highlighted-numpad-btn');
const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
if (!curr_action_is_highlighted) {
$btn.addClass('highlighted-numpad-btn');
}
if (this.prev_action === curr_action && curr_action_is_highlighted) {
// if Qty is pressed twice
$btn.removeClass('highlighted-numpad-btn');
}
if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) {
// Order: Qty -> Rate then remove Qty highlight
const prev_btn = $(`[data-button-value='${this.prev_action}']`);
prev_btn.removeClass('highlighted-numpad-btn');
}
if (!curr_action_is_action || curr_action === 'done') {
// if numbers are clicked
setTimeout(() => {
$btn.removeClass('highlighted-numpad-btn');
}, 200);
}
}
toggle_numpad(show) {
if (show) {
this.$totals_section.css('display', 'none');
this.$numpad_section.css('display', 'flex');
} else {
this.$totals_section.css('display', 'flex');
this.$numpad_section.css('display', 'none');
}
this.reset_numpad();
}
reset_numpad() {
this.numpad_value = '';
this.prev_action = undefined;
this.$numpad_section.find('.highlighted-numpad-btn').removeClass('highlighted-numpad-btn');
}
toggle_numpad_field_edit(fieldname) {
if (['qty', 'discount_percentage', 'rate'].includes(fieldname)) {
this.$numpad_section.find(`[data-button-value="${fieldname}"]`).click();
}
}
toggle_customer_info(show) {
if (show) {
const { customer } = this.customer_info || {};
this.$cart_container.css('display', 'none');
this.$customer_section.css({
'height': '100%',
'padding-top': '0px'
});
this.$customer_section.find('.customer-details').html(
`
${this.get_customer_image()}
Recent Transactions
`
);
// transactions need to be in diff div from sticky elem for scrolling
this.$customer_section.append(`
`);
this.render_customer_fields();
this.fetch_customer_transactions();
} else {
this.$cart_container.css('display', 'flex');
this.$customer_section.css({
'height': '',
'padding-top': ''
});
this.update_customer_section();
}
}
render_customer_fields() {
const $customer_form = this.$customer_section.find('.customer-fields-container');
const dfs = [{
fieldname: 'email_id',
label: __('Email'),
fieldtype: 'Data',
options: 'email',
placeholder: __("Enter customer's email")
},{
fieldname: 'mobile_no',
label: __('Phone Number'),
fieldtype: 'Data',
placeholder: __("Enter customer's phone number")
},{
fieldname: 'loyalty_program',
label: __('Loyalty Program'),
fieldtype: 'Link',
options: 'Loyalty Program',
placeholder: __("Select Loyalty Program")
},{
fieldname: 'loyalty_points',
label: __('Loyalty Points'),
fieldtype: 'Data',
read_only: 1
}];
const me = this;
dfs.forEach(df => {
this[`customer_${df.fieldname}_field`] = frappe.ui.form.make_control({
df: { ...df,
onchange: handle_customer_field_change,
},
parent: $customer_form.find(`.${df.fieldname}-field`),
render_input: true,
});
this[`customer_${df.fieldname}_field`].set_value(this.customer_info[df.fieldname]);
})
function handle_customer_field_change() {
const current_value = me.customer_info[this.df.fieldname];
const current_customer = me.customer_info.customer;
if (this.value && current_value != this.value && this.df.fieldname != 'loyalty_points') {
frappe.call({
method: 'erpnext.selling.page.point_of_sale.point_of_sale.set_customer_info',
args: {
fieldname: this.df.fieldname,
customer: current_customer,
value: this.value
},
callback: (r) => {
if(!r.exc) {
me.customer_info[this.df.fieldname] = this.value;
frappe.show_alert({
message: __("Customer contact updated successfully."),
indicator: 'green'
});
frappe.utils.play_sound("submit");
}
}
});
}
}
}
fetch_customer_transactions() {
frappe.db.get_list('POS Invoice', {
filters: { customer: this.customer_info.customer, docstatus: 1 },
fields: ['name', 'grand_total', 'status', 'posting_date', 'posting_time', 'currency'],
limit: 20
}).then((res) => {
const transaction_container = this.$customer_section.find('.customer-transactions');
if (!res.length) {
transaction_container.html(
`
No recent transactions found
`
)
return;
};
const elapsed_time = moment(res[0].posting_date+" "+res[0].posting_time).fromNow();
this.$customer_section.find('.customer-desc').html(`Last transacted ${elapsed_time}`);
res.forEach(invoice => {
const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma");
let indicator_color = {
'Paid': 'green',
'Draft': 'red',
'Return': 'gray',
'Consolidated': 'blue'
};
transaction_container.append(
`
${invoice.name}
${posting_datetime}
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
${invoice.status}
`
)
});
});
}
load_invoice() {
const frm = this.events.get_frm();
this.fetch_customer_details(frm.doc.customer).then(() => {
this.events.customer_details_updated(this.customer_info);
this.update_customer_section();
});
this.$cart_items_wrapper.html('');
if (frm.doc.items.length) {
frm.doc.items.forEach(item => {
this.update_item_html(item);
});
} else {
this.make_no_items_placeholder();
this.highlight_checkout_btn(false);
}
this.update_totals_section(frm);
if(frm.doc.docstatus === 1) {
this.$totals_section.find('.checkout-btn').css('display', 'none');
this.$totals_section.find('.edit-cart-btn').css('display', 'none');
} else {
this.$totals_section.find('.checkout-btn').css('display', 'flex');
this.$totals_section.find('.edit-cart-btn').css('display', 'none');
}
this.toggle_component(true);
}
toggle_component(show) {
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
}
}