2017-08-14 12:55:59 +00:00
|
|
|
/* global Clusterize */
|
2017-09-05 11:40:24 +00:00
|
|
|
frappe.provide('erpnext.pos');
|
2019-12-09 12:21:32 +00:00
|
|
|
frappe.provide('erpnext.queries');
|
2017-08-14 12:55:59 +00:00
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
|
2017-08-30 13:26:26 +00:00
|
|
|
frappe.ui.make_app_page({
|
2017-08-10 05:52:03 +00:00
|
|
|
parent: wrapper,
|
2019-08-23 11:02:58 +00:00
|
|
|
title: __('Point of Sale'),
|
2017-08-10 05:52:03 +00:00
|
|
|
single_column: true
|
|
|
|
});
|
|
|
|
|
2017-08-29 09:57:17 +00:00
|
|
|
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => {
|
2018-03-30 08:59:18 +00:00
|
|
|
if (r && !cint(r.use_pos_in_offline_mode)) {
|
2017-08-29 09:57:17 +00:00
|
|
|
// online
|
2017-09-05 11:40:24 +00:00
|
|
|
wrapper.pos = new erpnext.pos.PointOfSale(wrapper);
|
2017-08-29 09:57:17 +00:00
|
|
|
window.cur_pos = wrapper.pos;
|
|
|
|
} else {
|
|
|
|
// offline
|
2018-02-05 06:54:06 +00:00
|
|
|
frappe.flags.is_offline = true;
|
2017-08-29 09:57:17 +00:00
|
|
|
frappe.set_route('pos');
|
|
|
|
}
|
|
|
|
});
|
2017-08-21 09:35:33 +00:00
|
|
|
};
|
2017-08-10 05:52:03 +00:00
|
|
|
|
2017-09-26 07:57:06 +00:00
|
|
|
frappe.pages['point-of-sale'].refresh = function(wrapper) {
|
2017-09-27 06:46:58 +00:00
|
|
|
if (wrapper.pos) {
|
2018-03-28 07:03:15 +00:00
|
|
|
wrapper.pos.make_new_invoice();
|
2017-09-27 06:46:58 +00:00
|
|
|
}
|
2018-02-05 06:54:06 +00:00
|
|
|
|
|
|
|
if (frappe.flags.is_offline) {
|
|
|
|
frappe.set_route('pos');
|
|
|
|
}
|
2017-09-26 07:57:06 +00:00
|
|
|
}
|
|
|
|
|
2017-09-05 11:40:24 +00:00
|
|
|
erpnext.pos.PointOfSale = class PointOfSale {
|
2017-08-10 05:52:03 +00:00
|
|
|
constructor(wrapper) {
|
|
|
|
this.wrapper = $(wrapper).find('.layout-main-section');
|
|
|
|
this.page = wrapper.page;
|
|
|
|
|
|
|
|
const assets = [
|
2017-08-10 11:47:34 +00:00
|
|
|
'assets/erpnext/js/pos/clusterize.js',
|
2017-08-10 05:52:03 +00:00
|
|
|
'assets/erpnext/css/pos.css'
|
|
|
|
];
|
|
|
|
|
|
|
|
frappe.require(assets, () => {
|
2017-08-14 12:55:59 +00:00
|
|
|
this.make();
|
2017-08-10 05:52:03 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
make() {
|
2017-08-14 12:55:59 +00:00
|
|
|
return frappe.run_serially([
|
2017-11-06 13:12:37 +00:00
|
|
|
() => frappe.dom.freeze(),
|
2017-08-14 12:55:59 +00:00
|
|
|
() => {
|
|
|
|
this.prepare_dom();
|
|
|
|
this.prepare_menu();
|
|
|
|
this.set_online_status();
|
|
|
|
},
|
2017-10-05 08:01:29 +00:00
|
|
|
() => this.make_new_invoice(),
|
2019-04-14 14:09:11 +00:00
|
|
|
() => {
|
|
|
|
if(!this.frm.doc.company) {
|
|
|
|
this.setup_company()
|
|
|
|
.then((company) => {
|
|
|
|
this.frm.doc.company = company;
|
|
|
|
this.get_pos_profile();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2017-08-14 12:55:59 +00:00
|
|
|
() => {
|
2017-11-06 13:12:37 +00:00
|
|
|
frappe.dom.unfreeze();
|
2017-08-24 09:57:55 +00:00
|
|
|
},
|
2017-08-29 08:57:01 +00:00
|
|
|
() => this.page.set_title(__('Point of Sale'))
|
2017-08-14 12:55:59 +00:00
|
|
|
]);
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
2019-04-14 14:09:11 +00:00
|
|
|
get_pos_profile() {
|
|
|
|
return frappe.xcall("erpnext.stock.get_item_details.get_pos_profile",
|
|
|
|
{'company': this.frm.doc.company})
|
|
|
|
.then((r) => {
|
|
|
|
if(r) {
|
|
|
|
this.frm.doc.pos_profile = r.name;
|
|
|
|
this.set_pos_profile_data()
|
|
|
|
.then(() => {
|
|
|
|
this.on_change_pos_profile();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.raise_exception_for_pos_profile();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
set_online_status() {
|
|
|
|
this.connection_status = false;
|
|
|
|
this.page.set_indicator(__("Offline"), "grey");
|
|
|
|
frappe.call({
|
|
|
|
method: "frappe.handler.ping",
|
|
|
|
callback: r => {
|
|
|
|
if (r.message) {
|
|
|
|
this.connection_status = true;
|
|
|
|
this.page.set_indicator(__("Online"), "green");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-14 14:09:11 +00:00
|
|
|
raise_exception_for_pos_profile() {
|
|
|
|
setTimeout(() => frappe.set_route('List', 'POS Profile'), 2000);
|
|
|
|
frappe.throw(__("POS Profile is required to use Point-of-Sale"));
|
|
|
|
}
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
prepare_dom() {
|
2017-08-10 05:52:03 +00:00
|
|
|
this.wrapper.append(`
|
|
|
|
<div class="pos">
|
2017-08-10 11:47:34 +00:00
|
|
|
<section class="cart-container">
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
</section>
|
|
|
|
<section class="item-container">
|
|
|
|
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
|
|
|
|
make_cart() {
|
2017-08-14 12:55:59 +00:00
|
|
|
this.cart = new POSCart({
|
2017-08-23 06:45:10 +00:00
|
|
|
frm: this.frm,
|
2017-08-14 12:55:59 +00:00
|
|
|
wrapper: this.wrapper.find('.cart-container'),
|
|
|
|
events: {
|
2018-07-06 07:06:57 +00:00
|
|
|
on_customer_change: (customer) => {
|
|
|
|
this.frm.set_value('customer', customer);
|
|
|
|
},
|
2018-03-28 07:03:15 +00:00
|
|
|
on_field_change: (item_code, field, value, batch_no) => {
|
|
|
|
this.update_item_in_cart(item_code, field, value, batch_no);
|
2017-08-21 09:35:33 +00:00
|
|
|
},
|
|
|
|
on_numpad: (value) => {
|
2018-07-01 11:14:09 +00:00
|
|
|
if (value == __('Pay')) {
|
2017-08-21 09:35:33 +00:00
|
|
|
if (!this.payment) {
|
|
|
|
this.make_payment_modal();
|
2017-10-02 15:27:08 +00:00
|
|
|
} else {
|
|
|
|
this.frm.doc.payments.map(p => {
|
2017-10-15 13:39:14 +00:00
|
|
|
this.payment.dialog.set_value(p.mode_of_payment, p.amount);
|
2017-10-02 15:27:08 +00:00
|
|
|
});
|
|
|
|
|
2017-10-10 20:37:09 +00:00
|
|
|
this.payment.set_title();
|
2017-08-21 09:35:33 +00:00
|
|
|
}
|
|
|
|
this.payment.open_modal();
|
|
|
|
}
|
2017-08-23 07:42:12 +00:00
|
|
|
},
|
|
|
|
on_select_change: () => {
|
|
|
|
this.cart.numpad.set_inactive();
|
2018-01-12 08:56:44 +00:00
|
|
|
this.set_form_action();
|
2017-10-25 08:29:29 +00:00
|
|
|
},
|
|
|
|
get_item_details: (item_code) => {
|
|
|
|
return this.items.get(item_code);
|
2018-07-06 07:06:57 +00:00
|
|
|
},
|
|
|
|
get_loyalty_details: () => {
|
|
|
|
var me = this;
|
|
|
|
if (this.frm.doc.customer) {
|
|
|
|
frappe.call({
|
|
|
|
method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_loyalty_program_details",
|
|
|
|
args: {
|
|
|
|
"customer": me.frm.doc.customer,
|
[ Major ] Loyalty Program Fixes (#14863)
* fix checking of customer_group & territory of customer in loyalty program
* fetch and set applicable loyalty program
- in customer.py if found 1 program, set it or show a message to set it manually
- in sales invoice, if found 1 program for selected customer with no program set, set it else open a dialog with applicable options
* removed disabled field, added from_date & to_date
* loyalty program section made collapsible, added redeem check in it
* setting loyalty program improvised, manual selection if multiple found
* get_query added, amount calculation updated
* args passed rectified for expiry_date
* get loyalty_points logic improv, redemption_details logic added
* improv based on from/to date and other rectification
* code rectified based on different scenarios
- is_return, cancel, make loyalty points entry improv
2018-07-11 09:05:43 +00:00
|
|
|
"expiry_date": me.frm.doc.posting_date,
|
2018-07-06 07:06:57 +00:00
|
|
|
"company": me.frm.doc.company,
|
|
|
|
"silent": true
|
|
|
|
},
|
|
|
|
callback: function(r) {
|
|
|
|
if (r.message.loyalty_program && r.message.loyalty_points) {
|
|
|
|
me.cart.events.set_loyalty_details(r.message, true);
|
|
|
|
}
|
|
|
|
if (!r.message.loyalty_program) {
|
|
|
|
var loyalty_details = {
|
|
|
|
loyalty_points: 0,
|
|
|
|
loyalty_program: '',
|
|
|
|
expense_account: '',
|
|
|
|
cost_center: ''
|
|
|
|
}
|
|
|
|
me.cart.events.set_loyalty_details(loyalty_details, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
set_loyalty_details: (details, view_status) => {
|
|
|
|
if (view_status) {
|
|
|
|
this.cart.available_loyalty_points.$wrapper.removeClass("hide");
|
|
|
|
} else {
|
|
|
|
this.cart.available_loyalty_points.$wrapper.addClass("hide");
|
|
|
|
}
|
|
|
|
this.cart.available_loyalty_points.set_value(details.loyalty_points);
|
|
|
|
this.cart.available_loyalty_points.refresh_input();
|
|
|
|
this.frm.set_value("loyalty_program", details.loyalty_program);
|
|
|
|
this.frm.set_value("loyalty_redemption_account", details.expense_account);
|
|
|
|
this.frm.set_value("loyalty_redemption_cost_center", details.cost_center);
|
2017-08-14 12:55:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-01-25 11:11:01 +00:00
|
|
|
|
|
|
|
frappe.ui.form.on('Sales Invoice', 'selling_price_list', (frm) => {
|
2019-02-08 11:13:09 +00:00
|
|
|
if(this.items && frm.doc.pos_profile) {
|
2019-01-25 11:11:01 +00:00
|
|
|
this.items.reset_items();
|
|
|
|
}
|
|
|
|
})
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 11:39:34 +00:00
|
|
|
toggle_editing(flag) {
|
|
|
|
let disabled;
|
|
|
|
if (flag !== undefined) {
|
|
|
|
disabled = !flag;
|
|
|
|
} else {
|
|
|
|
disabled = this.frm.doc.docstatus == 1 ? true: false;
|
|
|
|
}
|
|
|
|
const pointer_events = disabled ? 'none' : 'inherit';
|
2017-08-24 09:57:55 +00:00
|
|
|
|
2017-08-24 11:39:34 +00:00
|
|
|
this.wrapper.find('input, button, select').prop("disabled", disabled);
|
|
|
|
this.wrapper.find('.number-pad-container').toggleClass("hide", disabled);
|
2017-08-24 09:57:55 +00:00
|
|
|
|
2017-08-24 11:39:34 +00:00
|
|
|
this.wrapper.find('.cart-container').css('pointer-events', pointer_events);
|
|
|
|
this.wrapper.find('.item-container').css('pointer-events', pointer_events);
|
2017-08-24 09:57:55 +00:00
|
|
|
|
|
|
|
this.page.clear_actions();
|
2017-08-23 06:45:10 +00:00
|
|
|
}
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
make_items() {
|
2017-08-14 12:55:59 +00:00
|
|
|
this.items = new POSItems({
|
2017-08-10 11:47:34 +00:00
|
|
|
wrapper: this.wrapper.find('.item-container'),
|
2017-11-28 10:41:15 +00:00
|
|
|
frm: this.frm,
|
2017-08-10 11:47:34 +00:00
|
|
|
events: {
|
2017-08-29 09:57:17 +00:00
|
|
|
update_cart: (item, field, value) => {
|
2017-08-21 09:35:33 +00:00
|
|
|
if(!this.frm.doc.customer) {
|
2017-08-14 12:55:59 +00:00
|
|
|
frappe.throw(__('Please select a customer'));
|
|
|
|
}
|
2017-08-29 09:57:17 +00:00
|
|
|
this.update_item_in_cart(item, field, value);
|
2017-08-23 07:42:12 +00:00
|
|
|
this.cart && this.cart.unselect_all();
|
2017-08-14 12:55:59 +00:00
|
|
|
}
|
2017-08-10 11:47:34 +00:00
|
|
|
}
|
2017-08-10 05:52:03 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
update_item_in_cart(item_code, field='qty', value=1, batch_no) {
|
2017-11-06 13:12:37 +00:00
|
|
|
frappe.dom.freeze();
|
2018-03-28 07:03:15 +00:00
|
|
|
if(this.cart.exists(item_code, batch_no)) {
|
|
|
|
const search_field = batch_no ? 'batch_no' : 'item_code';
|
|
|
|
const search_value = batch_no || item_code;
|
|
|
|
const item = this.frm.doc.items.find(i => i[search_field] === search_value);
|
2017-09-01 09:58:44 +00:00
|
|
|
frappe.flags.hide_serial_batch_dialog = false;
|
2017-08-23 10:55:16 +00:00
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
if (typeof value === 'string' && !in_list(['serial_no', 'batch_no'], field)) {
|
2017-08-23 10:55:16 +00:00
|
|
|
// value can be of type '+1' or '-1'
|
|
|
|
value = item[field] + flt(value);
|
|
|
|
}
|
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
if(field === 'serial_no') {
|
|
|
|
value = item.serial_no + '\n'+ value;
|
2017-08-24 09:57:55 +00:00
|
|
|
}
|
|
|
|
|
2017-11-30 06:08:53 +00:00
|
|
|
// if actual_batch_qty and actual_qty if there is only one batch. In such
|
|
|
|
// a case, no point showing the dialog
|
2017-12-14 10:36:23 +00:00
|
|
|
const show_dialog = item.has_serial_no || item.has_batch_no;
|
|
|
|
|
|
|
|
if (show_dialog && field == 'qty' && ((!item.batch_no && item.has_batch_no) ||
|
|
|
|
(item.has_serial_no) || (item.actual_batch_qty != item.actual_qty)) ) {
|
2017-08-29 09:57:17 +00:00
|
|
|
this.select_batch_and_serial_no(item);
|
2017-08-28 11:49:28 +00:00
|
|
|
} else {
|
|
|
|
this.update_item_in_frm(item, field, value)
|
|
|
|
.then(() => {
|
2019-04-01 08:59:55 +00:00
|
|
|
frappe.dom.unfreeze();
|
2019-04-01 11:02:24 +00:00
|
|
|
frappe.run_serially([
|
|
|
|
() => {
|
|
|
|
let items = this.frm.doc.items.map(item => item.name);
|
|
|
|
if (items && items.length > 0 && items.includes(item.name)) {
|
|
|
|
this.frm.doc.items.forEach(item_row => {
|
|
|
|
// update cart
|
|
|
|
this.on_qty_change(item_row);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.on_qty_change(item);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
() => this.post_qty_change(item)
|
|
|
|
]);
|
2017-08-30 13:26:26 +00:00
|
|
|
});
|
2017-08-28 11:49:28 +00:00
|
|
|
}
|
2017-08-14 12:55:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
let args = { item_code: item_code };
|
|
|
|
if (in_list(['serial_no', 'batch_no'], field)) {
|
|
|
|
args[field] = value;
|
|
|
|
}
|
2020-04-16 17:23:47 +00:00
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
// add to cur_frm
|
2017-08-28 11:49:28 +00:00
|
|
|
const item = this.frm.add_child('items', args);
|
2017-09-01 09:58:44 +00:00
|
|
|
frappe.flags.hide_serial_batch_dialog = true;
|
2017-12-18 07:02:49 +00:00
|
|
|
|
|
|
|
frappe.run_serially([
|
2019-03-29 08:04:39 +00:00
|
|
|
() => {
|
2019-12-16 09:19:59 +00:00
|
|
|
return this.frm.script_manager.trigger('item_code', item.doctype, item.name)
|
2019-03-29 08:04:39 +00:00
|
|
|
.then(() => {
|
|
|
|
this.frm.script_manager.trigger('qty', item.doctype, item.name)
|
|
|
|
.then(() => {
|
2019-04-01 11:02:24 +00:00
|
|
|
frappe.run_serially([
|
|
|
|
() => {
|
|
|
|
let items = this.frm.doc.items.map(i => i.name);
|
|
|
|
if (items && items.length > 0 && items.includes(item.name)) {
|
|
|
|
this.frm.doc.items.forEach(item_row => {
|
|
|
|
// update cart
|
|
|
|
this.on_qty_change(item_row);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.on_qty_change(item);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
() => this.post_qty_change(item)
|
|
|
|
]);
|
2019-03-29 08:04:39 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
2017-12-18 07:02:49 +00:00
|
|
|
() => {
|
2017-09-01 09:58:44 +00:00
|
|
|
const show_dialog = item.has_serial_no || item.has_batch_no;
|
2017-11-30 06:08:53 +00:00
|
|
|
|
|
|
|
// if actual_batch_qty and actual_qty if then there is only one batch. In such
|
|
|
|
// a case, no point showing the dialog
|
2017-12-14 10:36:23 +00:00
|
|
|
if (show_dialog && field == 'qty' && ((!item.batch_no && item.has_batch_no) ||
|
|
|
|
(item.has_serial_no) || (item.actual_batch_qty != item.actual_qty)) ) {
|
2017-09-01 09:58:44 +00:00
|
|
|
// check has serial no/batch no and update cart
|
|
|
|
this.select_batch_and_serial_no(item);
|
|
|
|
}
|
2017-12-18 07:02:49 +00:00
|
|
|
}
|
|
|
|
]);
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
2019-04-01 08:59:55 +00:00
|
|
|
on_qty_change(item) {
|
|
|
|
frappe.run_serially([
|
|
|
|
() => this.update_cart_data(item),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2019-04-01 11:02:24 +00:00
|
|
|
post_qty_change(item) {
|
|
|
|
this.cart.update_taxes_and_totals();
|
|
|
|
this.cart.update_grand_total();
|
|
|
|
this.cart.update_qty_total();
|
|
|
|
this.cart.scroll_to_item(item.item_code);
|
|
|
|
this.set_form_action();
|
|
|
|
}
|
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
select_batch_and_serial_no(row) {
|
2017-12-14 10:36:23 +00:00
|
|
|
frappe.dom.unfreeze();
|
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
erpnext.show_serial_batch_selector(this.frm, row, () => {
|
|
|
|
this.frm.doc.items.forEach(item => {
|
|
|
|
this.update_item_in_frm(item, 'qty', item.qty)
|
|
|
|
.then(() => {
|
|
|
|
// update cart
|
|
|
|
frappe.run_serially([
|
|
|
|
() => {
|
|
|
|
if (item.qty === 0) {
|
|
|
|
frappe.model.clear_doc(item.doctype, item.name);
|
|
|
|
}
|
|
|
|
},
|
2019-04-01 11:02:24 +00:00
|
|
|
() => this.update_cart_data(item),
|
|
|
|
() => this.post_qty_change(item)
|
2018-03-28 07:03:15 +00:00
|
|
|
]);
|
|
|
|
});
|
|
|
|
})
|
2017-12-14 10:36:23 +00:00
|
|
|
}, () => {
|
2018-03-28 07:03:15 +00:00
|
|
|
this.on_close(row);
|
2017-08-30 13:26:26 +00:00
|
|
|
}, true);
|
2017-08-28 11:49:28 +00:00
|
|
|
}
|
|
|
|
|
2017-12-14 10:36:23 +00:00
|
|
|
on_close(item) {
|
2018-03-28 07:03:15 +00:00
|
|
|
if (!this.cart.exists(item.item_code, item.batch_no) && item.qty) {
|
2017-12-14 10:36:23 +00:00
|
|
|
frappe.model.clear_doc(item.doctype, item.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
update_cart_data(item) {
|
|
|
|
this.cart.add_item(item);
|
2017-11-06 13:12:37 +00:00
|
|
|
frappe.dom.unfreeze();
|
2017-08-28 11:49:28 +00:00
|
|
|
}
|
|
|
|
|
2017-08-23 10:55:16 +00:00
|
|
|
update_item_in_frm(item, field, value) {
|
2017-10-05 10:27:58 +00:00
|
|
|
if (field == 'qty' && value < 0) {
|
|
|
|
frappe.msgprint(__("Quantity must be positive"));
|
|
|
|
value = item.qty;
|
2018-02-20 05:48:01 +00:00
|
|
|
} else {
|
2018-02-26 07:34:26 +00:00
|
|
|
if (in_list(["qty", "serial_no", "batch"], field)) {
|
|
|
|
item[field] = value;
|
|
|
|
if (field == "serial_no" && value) {
|
|
|
|
let serial_nos = value.split("\n");
|
|
|
|
item["qty"] = serial_nos.filter(d => {
|
|
|
|
return d!=="";
|
|
|
|
}).length;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return frappe.model.set_value(item.doctype, item.name, field, value);
|
2018-02-20 05:48:01 +00:00
|
|
|
}
|
2017-10-05 10:27:58 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 05:48:01 +00:00
|
|
|
return this.frm.script_manager.trigger('qty', item.doctype, item.name)
|
|
|
|
.then(() => {
|
|
|
|
if (field === 'qty' && item.qty === 0) {
|
|
|
|
frappe.model.clear_doc(item.doctype, item.name);
|
|
|
|
}
|
|
|
|
})
|
2017-08-28 11:49:28 +00:00
|
|
|
|
2017-10-26 09:04:15 +00:00
|
|
|
return Promise.resolve();
|
2017-08-23 10:55:16 +00:00
|
|
|
}
|
|
|
|
|
2017-08-21 09:35:33 +00:00
|
|
|
make_payment_modal() {
|
2017-08-24 09:57:55 +00:00
|
|
|
this.payment = new Payment({
|
|
|
|
frm: this.frm,
|
|
|
|
events: {
|
|
|
|
submit_form: () => {
|
2017-08-30 13:26:26 +00:00
|
|
|
this.submit_sales_invoice();
|
2017-08-24 09:57:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
submit_sales_invoice() {
|
2018-02-20 05:48:01 +00:00
|
|
|
this.frm.savesubmit()
|
|
|
|
.then((r) => {
|
|
|
|
if (r && r.doc) {
|
|
|
|
this.frm.doc.docstatus = r.doc.docstatus;
|
2017-08-24 11:39:34 +00:00
|
|
|
frappe.show_alert({
|
|
|
|
indicator: 'green',
|
2018-02-20 05:48:01 +00:00
|
|
|
message: __(`Sales invoice ${r.doc.name} created succesfully`)
|
2017-08-24 11:39:34 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
this.toggle_editing();
|
|
|
|
this.set_form_action();
|
2017-11-20 06:01:53 +00:00
|
|
|
this.set_primary_action_in_modal();
|
2017-08-24 11:39:34 +00:00
|
|
|
}
|
|
|
|
});
|
2017-08-21 09:35:33 +00:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:01:53 +00:00
|
|
|
set_primary_action_in_modal() {
|
2018-02-20 05:48:01 +00:00
|
|
|
if (!this.frm.msgbox) {
|
|
|
|
this.frm.msgbox = frappe.msgprint(
|
|
|
|
`<a class="btn btn-primary" onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">
|
|
|
|
${__('Print')}</a>
|
|
|
|
<a class="btn btn-default">
|
|
|
|
${__('New')}</a>`
|
|
|
|
);
|
|
|
|
|
|
|
|
$(this.frm.msgbox.body).find('.btn-default').on('click', () => {
|
|
|
|
this.frm.msgbox.hide();
|
|
|
|
this.make_new_invoice();
|
|
|
|
})
|
|
|
|
}
|
2017-11-20 06:01:53 +00:00
|
|
|
}
|
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
change_pos_profile() {
|
2017-09-29 10:45:15 +00:00
|
|
|
return new Promise((resolve) => {
|
2019-11-01 17:05:08 +00:00
|
|
|
const on_submit = ({ company, pos_profile, set_as_default }) => {
|
2017-11-28 10:41:15 +00:00
|
|
|
if (pos_profile) {
|
2018-01-08 09:52:22 +00:00
|
|
|
this.pos_profile = pos_profile;
|
2017-11-28 10:41:15 +00:00
|
|
|
}
|
2017-10-31 16:26:16 +00:00
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
if (set_as_default) {
|
|
|
|
frappe.call({
|
|
|
|
method: "erpnext.accounts.doctype.pos_profile.pos_profile.set_default_profile",
|
|
|
|
args: {
|
|
|
|
'pos_profile': pos_profile,
|
2019-11-01 17:05:08 +00:00
|
|
|
'company': company
|
2017-10-30 11:42:50 +00:00
|
|
|
}
|
2017-11-28 10:41:15 +00:00
|
|
|
}).then(() => {
|
|
|
|
this.on_change_pos_profile();
|
2017-09-29 10:45:15 +00:00
|
|
|
});
|
2017-11-08 09:04:38 +00:00
|
|
|
} else {
|
2017-11-28 10:41:15 +00:00
|
|
|
this.on_change_pos_profile();
|
2017-09-29 10:45:15 +00:00
|
|
|
}
|
2017-11-28 10:41:15 +00:00
|
|
|
}
|
|
|
|
|
2019-11-15 11:24:26 +00:00
|
|
|
|
|
|
|
let me = this;
|
|
|
|
|
|
|
|
var dialog = frappe.prompt([{
|
|
|
|
fieldtype: 'Link',
|
|
|
|
label: __('Company'),
|
|
|
|
options: 'Company',
|
|
|
|
fieldname: 'company',
|
|
|
|
default: me.frm.doc.company,
|
|
|
|
reqd: 1,
|
|
|
|
onchange: function(e) {
|
|
|
|
me.get_default_pos_profile(this.value).then((r) => {
|
2019-11-19 12:51:53 +00:00
|
|
|
dialog.set_value('pos_profile', (r && r.name)? r.name : '');
|
2019-11-15 11:24:26 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Link',
|
|
|
|
label: __('POS Profile'),
|
|
|
|
options: 'POS Profile',
|
|
|
|
fieldname: 'pos_profile',
|
|
|
|
default: me.frm.doc.pos_profile,
|
|
|
|
reqd: 1,
|
|
|
|
get_query: () => {
|
|
|
|
return {
|
|
|
|
query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
|
|
|
|
filters: {
|
|
|
|
company: dialog.get_value('company')
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
fieldtype: 'Check',
|
|
|
|
label: __('Set as default'),
|
|
|
|
fieldname: 'set_as_default'
|
|
|
|
}],
|
2017-11-28 10:41:15 +00:00
|
|
|
on_submit,
|
|
|
|
__('Select POS Profile')
|
|
|
|
);
|
2017-09-29 10:45:15 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
on_change_pos_profile() {
|
2018-01-08 09:52:22 +00:00
|
|
|
return frappe.run_serially([
|
|
|
|
() => this.make_sales_invoice_frm(),
|
|
|
|
() => {
|
|
|
|
this.frm.doc.pos_profile = this.pos_profile;
|
|
|
|
this.set_pos_profile_data()
|
|
|
|
.then(() => {
|
|
|
|
this.reset_cart();
|
|
|
|
if (this.items) {
|
|
|
|
this.items.reset_items();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
]);
|
2017-11-28 10:41:15 +00:00
|
|
|
}
|
|
|
|
|
2019-11-15 11:24:26 +00:00
|
|
|
get_default_pos_profile(company) {
|
|
|
|
return frappe.xcall("erpnext.stock.get_item_details.get_pos_profile",
|
|
|
|
{'company': company})
|
2017-11-03 06:05:49 +00:00
|
|
|
}
|
2017-10-09 07:18:29 +00:00
|
|
|
|
|
|
|
setup_company() {
|
|
|
|
return new Promise(resolve => {
|
2019-04-14 14:09:11 +00:00
|
|
|
if(!this.frm.doc.company) {
|
2017-10-09 07:18:29 +00:00
|
|
|
frappe.prompt({fieldname:"company", options: "Company", fieldtype:"Link",
|
|
|
|
label: __("Select Company"), reqd: 1}, (data) => {
|
|
|
|
this.company = data.company;
|
|
|
|
resolve(this.company);
|
|
|
|
}, __("Select Company"));
|
|
|
|
} else {
|
2019-04-05 04:24:55 +00:00
|
|
|
resolve();
|
2017-08-29 09:57:17 +00:00
|
|
|
}
|
2017-10-09 07:18:29 +00:00
|
|
|
})
|
2017-08-10 11:47:34 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 09:57:55 +00:00
|
|
|
make_new_invoice() {
|
|
|
|
return frappe.run_serially([
|
2017-11-29 11:12:02 +00:00
|
|
|
() => this.make_sales_invoice_frm(),
|
|
|
|
() => this.set_pos_profile_data(),
|
2017-08-24 09:57:55 +00:00
|
|
|
() => {
|
2017-11-29 11:12:02 +00:00
|
|
|
if (this.cart) {
|
|
|
|
this.cart.frm = this.frm;
|
|
|
|
this.cart.reset();
|
2019-12-09 12:21:32 +00:00
|
|
|
this.cart.reset_pos_field_value();
|
2017-11-29 11:12:02 +00:00
|
|
|
} else {
|
|
|
|
this.make_items();
|
|
|
|
this.make_cart();
|
|
|
|
}
|
|
|
|
this.toggle_editing(true);
|
2017-11-18 11:28:58 +00:00
|
|
|
},
|
2017-08-24 09:57:55 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
reset_cart() {
|
|
|
|
this.cart.frm = this.frm;
|
|
|
|
this.cart.reset();
|
|
|
|
this.items.reset_search_field();
|
|
|
|
}
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
make_sales_invoice_frm() {
|
2017-08-24 11:39:34 +00:00
|
|
|
const doctype = 'Sales Invoice';
|
2017-08-10 11:47:34 +00:00
|
|
|
return new Promise(resolve => {
|
2017-08-24 11:39:34 +00:00
|
|
|
if (this.frm) {
|
2017-11-28 10:41:15 +00:00
|
|
|
this.frm = get_frm(this.frm);
|
2019-04-14 14:09:11 +00:00
|
|
|
if(this.company) {
|
|
|
|
this.frm.doc.company = this.company;
|
|
|
|
}
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
resolve();
|
2017-08-24 11:39:34 +00:00
|
|
|
} else {
|
|
|
|
frappe.model.with_doctype(doctype, () => {
|
2017-11-28 10:41:15 +00:00
|
|
|
this.frm = get_frm();
|
2017-08-24 11:39:34 +00:00
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
}
|
2017-08-10 11:47:34 +00:00
|
|
|
});
|
2017-08-24 11:39:34 +00:00
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
function get_frm(_frm) {
|
2017-08-24 11:39:34 +00:00
|
|
|
const page = $('<div>');
|
2019-05-22 11:22:36 +00:00
|
|
|
const frm = _frm || new frappe.ui.form.Form(doctype, page, false);
|
2017-08-24 11:39:34 +00:00
|
|
|
const name = frappe.model.make_new_doc_and_get_name(doctype, true);
|
|
|
|
frm.refresh(name);
|
|
|
|
frm.doc.items = [];
|
2017-11-18 11:28:58 +00:00
|
|
|
frm.doc.is_pos = 1;
|
2019-04-14 14:09:11 +00:00
|
|
|
|
2017-08-24 11:39:34 +00:00
|
|
|
return frm;
|
|
|
|
}
|
2017-08-10 11:47:34 +00:00
|
|
|
}
|
|
|
|
|
2017-11-18 11:28:58 +00:00
|
|
|
set_pos_profile_data() {
|
2019-04-05 04:24:55 +00:00
|
|
|
if (this.company) {
|
|
|
|
this.frm.doc.company = this.company;
|
|
|
|
}
|
2017-12-11 09:27:43 +00:00
|
|
|
|
2019-04-14 14:09:11 +00:00
|
|
|
if (!this.frm.doc.company) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-18 11:28:58 +00:00
|
|
|
return new Promise(resolve => {
|
|
|
|
return this.frm.call({
|
|
|
|
doc: this.frm.doc,
|
|
|
|
method: "set_missing_values",
|
|
|
|
}).then((r) => {
|
|
|
|
if(!r.exc) {
|
2017-11-28 10:41:15 +00:00
|
|
|
if (!this.frm.doc.pos_profile) {
|
|
|
|
frappe.dom.unfreeze();
|
2019-04-14 14:09:11 +00:00
|
|
|
this.raise_exception_for_pos_profile();
|
2017-11-28 10:41:15 +00:00
|
|
|
}
|
2017-11-18 11:28:58 +00:00
|
|
|
this.frm.script_manager.trigger("update_stock");
|
|
|
|
frappe.model.set_default_values(this.frm.doc);
|
|
|
|
this.frm.cscript.calculate_taxes_and_totals();
|
2017-11-29 11:12:02 +00:00
|
|
|
|
|
|
|
if (r.message) {
|
2018-09-06 07:55:52 +00:00
|
|
|
this.frm.meta.default_print_format = r.message.print_format || "";
|
2018-01-30 05:09:46 +00:00
|
|
|
this.frm.allow_edit_rate = r.message.allow_edit_rate;
|
2018-02-02 06:52:58 +00:00
|
|
|
this.frm.allow_edit_discount = r.message.allow_edit_discount;
|
2018-12-24 09:09:30 +00:00
|
|
|
this.frm.doc.campaign = r.message.campaign;
|
2019-12-31 11:37:37 +00:00
|
|
|
this.frm.allow_print_before_pay = r.message.allow_print_before_pay;
|
2017-11-29 11:12:02 +00:00
|
|
|
}
|
2017-11-18 11:28:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resolve();
|
2017-11-28 10:41:15 +00:00
|
|
|
});
|
|
|
|
});
|
2017-11-18 11:28:58 +00:00
|
|
|
}
|
|
|
|
|
2017-08-10 11:47:34 +00:00
|
|
|
prepare_menu() {
|
2017-08-24 09:57:55 +00:00
|
|
|
var me = this;
|
2017-08-10 11:47:34 +00:00
|
|
|
this.page.clear_menu();
|
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
this.page.add_menu_item(__("Form View"), function () {
|
|
|
|
frappe.model.sync(me.frm.doc);
|
|
|
|
frappe.set_route("Form", me.frm.doc.doctype, me.frm.doc.name);
|
|
|
|
});
|
|
|
|
|
2017-08-10 11:47:34 +00:00
|
|
|
this.page.add_menu_item(__("POS Profile"), function () {
|
|
|
|
frappe.set_route('List', 'POS Profile');
|
2017-08-10 05:52:03 +00:00
|
|
|
});
|
2017-08-29 09:57:17 +00:00
|
|
|
|
|
|
|
this.page.add_menu_item(__('POS Settings'), function() {
|
|
|
|
frappe.set_route('Form', 'POS Settings');
|
|
|
|
});
|
2017-11-28 10:41:15 +00:00
|
|
|
|
|
|
|
this.page.add_menu_item(__('Change POS Profile'), function() {
|
|
|
|
me.change_pos_profile();
|
|
|
|
});
|
2018-06-06 04:52:13 +00:00
|
|
|
this.page.add_menu_item(__('Close the POS'), function() {
|
|
|
|
var voucher = frappe.model.get_new_doc('POS Closing Voucher');
|
|
|
|
voucher.pos_profile = me.frm.doc.pos_profile;
|
|
|
|
voucher.user = frappe.session.user;
|
|
|
|
voucher.company = me.frm.doc.company;
|
|
|
|
voucher.period_start_date = me.frm.doc.posting_date;
|
|
|
|
voucher.period_end_date = me.frm.doc.posting_date;
|
|
|
|
voucher.posting_date = me.frm.doc.posting_date;
|
|
|
|
frappe.set_route('Form', 'POS Closing Voucher', voucher.name);
|
|
|
|
});
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
2017-08-24 09:57:55 +00:00
|
|
|
|
2017-08-24 11:39:34 +00:00
|
|
|
set_form_action() {
|
2019-12-31 11:37:37 +00:00
|
|
|
if(this.frm.doc.docstatus == 1 || (this.frm.allow_print_before_pay == 1 && this.frm.doc.items.length > 0)){
|
2018-01-12 20:13:48 +00:00
|
|
|
this.page.set_secondary_action(__("Print"), async() => {
|
2018-01-06 10:08:39 +00:00
|
|
|
if(this.frm.doc.docstatus != 1 ){
|
2018-01-12 20:13:48 +00:00
|
|
|
await this.frm.save();
|
2018-01-06 10:08:39 +00:00
|
|
|
}
|
2018-01-12 20:13:48 +00:00
|
|
|
this.frm.print_preview.printit(true);
|
2018-01-02 12:55:08 +00:00
|
|
|
});
|
2018-01-06 10:08:39 +00:00
|
|
|
}
|
2018-01-12 08:56:44 +00:00
|
|
|
if(this.frm.doc.items.length == 0){
|
|
|
|
this.page.clear_secondary_action();
|
|
|
|
}
|
2018-01-06 12:27:18 +00:00
|
|
|
|
2018-01-06 10:08:39 +00:00
|
|
|
if (this.frm.doc.docstatus == 1) {
|
2018-01-06 12:23:15 +00:00
|
|
|
this.page.set_primary_action(__("New"), () => {
|
|
|
|
this.make_new_invoice();
|
|
|
|
});
|
2018-01-02 12:55:08 +00:00
|
|
|
this.page.add_menu_item(__("Email"), () => {
|
|
|
|
this.frm.email_doc();
|
|
|
|
});
|
|
|
|
}
|
2017-08-24 09:57:55 +00:00
|
|
|
}
|
2017-09-05 11:59:45 +00:00
|
|
|
};
|
2017-08-10 05:52:03 +00:00
|
|
|
|
2018-07-01 11:14:09 +00:00
|
|
|
const [Qty,Disc,Rate,Del,Pay] = [__("Qty"), __('Disc'), __('Rate'), __('Del'), __('Pay')];
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
class POSCart {
|
2017-11-28 10:41:15 +00:00
|
|
|
constructor({frm, wrapper, events}) {
|
2017-08-23 06:45:10 +00:00
|
|
|
this.frm = frm;
|
2017-10-25 08:29:29 +00:00
|
|
|
this.item_data = {};
|
2017-08-10 05:52:03 +00:00
|
|
|
this.wrapper = wrapper;
|
2017-08-14 12:55:59 +00:00
|
|
|
this.events = events;
|
2017-08-10 05:52:03 +00:00
|
|
|
this.make();
|
2017-08-14 12:55:59 +00:00
|
|
|
this.bind_events();
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
make() {
|
2017-08-10 11:47:34 +00:00
|
|
|
this.make_dom();
|
|
|
|
this.make_customer_field();
|
2019-12-09 12:21:32 +00:00
|
|
|
this.make_pos_fields();
|
2018-07-06 07:06:57 +00:00
|
|
|
this.make_loyalty_points();
|
2017-08-14 12:55:59 +00:00
|
|
|
this.make_numpad();
|
2017-08-10 11:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
make_dom() {
|
2017-08-10 05:52:03 +00:00
|
|
|
this.wrapper.append(`
|
2017-08-24 09:57:55 +00:00
|
|
|
<div class="pos-cart">
|
|
|
|
<div class="customer-field">
|
|
|
|
</div>
|
2019-12-09 12:21:32 +00:00
|
|
|
<div class="pos-field-section" style="margin-bottom:12px; display:none">
|
|
|
|
<a class="h6 uppercase more-fields-section" disabled> ${__("More Information")} </a>
|
|
|
|
<i class="octicon octicon-chevron-down pos-fields-octicon collapse-indicator"
|
|
|
|
style="color:#cacaca; cursor: pointer"></i>
|
|
|
|
<div class="pos-fields" style ="margin-top:12px">
|
|
|
|
</div>
|
|
|
|
</div>
|
2017-08-24 09:57:55 +00:00
|
|
|
<div class="cart-wrapper">
|
|
|
|
<div class="list-item-table">
|
|
|
|
<div class="list-item list-item--head">
|
2017-08-30 13:26:26 +00:00
|
|
|
<div class="list-item__content list-item__content--flex-1.5 text-muted">${__('Item Name')}</div>
|
2017-08-24 09:57:55 +00:00
|
|
|
<div class="list-item__content text-muted text-right">${__('Quantity')}</div>
|
|
|
|
<div class="list-item__content text-muted text-right">${__('Discount')}</div>
|
|
|
|
<div class="list-item__content text-muted text-right">${__('Rate')}</div>
|
|
|
|
</div>
|
|
|
|
<div class="cart-items">
|
|
|
|
<div class="empty-state">
|
2018-07-01 11:14:09 +00:00
|
|
|
<span>${__('No Items added to cart')}</span>
|
2017-08-24 09:57:55 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2017-08-27 08:26:33 +00:00
|
|
|
<div class="taxes-and-totals">
|
|
|
|
${this.get_taxes_and_totals()}
|
|
|
|
</div>
|
2018-02-01 12:26:24 +00:00
|
|
|
<div class="discount-amount">`+
|
|
|
|
(!this.frm.allow_edit_discount ? `` : `${this.get_discount_amount()}`)+
|
|
|
|
`</div>
|
2017-08-27 08:26:33 +00:00
|
|
|
<div class="grand-total">
|
|
|
|
${this.get_grand_total()}
|
|
|
|
</div>
|
2018-02-08 10:50:46 +00:00
|
|
|
<div class="quantity-total">
|
|
|
|
${this.get_item_qty_total()}
|
|
|
|
</div>
|
2017-08-10 05:52:03 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2018-07-06 07:06:57 +00:00
|
|
|
<div class="row">
|
|
|
|
<div class="number-pad-container col-sm-6"></div>
|
|
|
|
<div class="col-sm-6 loyalty-program-section">
|
|
|
|
<div class="loyalty-program-field"> </div>
|
|
|
|
</div>
|
2017-08-23 10:55:16 +00:00
|
|
|
</div>
|
2017-08-10 05:52:03 +00:00
|
|
|
</div>
|
|
|
|
`);
|
2018-07-06 07:06:57 +00:00
|
|
|
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
this.$cart_items = this.wrapper.find('.cart-items');
|
2017-08-24 11:39:34 +00:00
|
|
|
this.$empty_state = this.wrapper.find('.cart-items .empty-state');
|
|
|
|
this.$taxes_and_totals = this.wrapper.find('.taxes-and-totals');
|
2017-08-27 08:26:33 +00:00
|
|
|
this.$discount_amount = this.wrapper.find('.discount-amount');
|
|
|
|
this.$grand_total = this.wrapper.find('.grand-total');
|
2018-02-08 10:50:46 +00:00
|
|
|
this.$qty_total = this.wrapper.find('.quantity-total');
|
2018-07-06 07:06:57 +00:00
|
|
|
// this.$loyalty_button = this.wrapper.find('.loyalty-button');
|
|
|
|
|
|
|
|
// this.$loyalty_button.on('click', () => {
|
|
|
|
// this.loyalty_button.show();
|
|
|
|
// })
|
2017-08-27 08:26:33 +00:00
|
|
|
|
|
|
|
this.toggle_taxes_and_totals(false);
|
|
|
|
this.$grand_total.on('click', () => {
|
|
|
|
this.toggle_taxes_and_totals();
|
|
|
|
});
|
2017-08-24 11:39:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reset() {
|
|
|
|
this.$cart_items.find('.list-item').remove();
|
|
|
|
this.$empty_state.show();
|
|
|
|
this.$taxes_and_totals.html(this.get_taxes_and_totals());
|
|
|
|
this.numpad && this.numpad.reset_value();
|
|
|
|
this.customer_field.set_value("");
|
2018-02-20 05:48:01 +00:00
|
|
|
this.frm.msgbox = "";
|
2017-10-02 15:27:08 +00:00
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
let total_item_qty = 0.0;
|
|
|
|
this.frm.set_value("pos_total_qty",total_item_qty);
|
|
|
|
|
2018-02-02 06:52:58 +00:00
|
|
|
this.$discount_amount.find('input:text').val('');
|
2017-10-02 15:27:08 +00:00
|
|
|
this.wrapper.find('.grand-total-value').text(
|
|
|
|
format_currency(this.frm.doc.grand_total, this.frm.currency));
|
2018-01-12 10:58:16 +00:00
|
|
|
this.wrapper.find('.rounded-total-value').text(
|
|
|
|
format_currency(this.frm.doc.rounded_total, this.frm.currency));
|
2018-03-28 07:03:15 +00:00
|
|
|
this.$qty_total.find(".quantity-total").text(total_item_qty);
|
2017-10-02 15:27:08 +00:00
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
const customer = this.frm.doc.customer;
|
2017-10-02 15:27:08 +00:00
|
|
|
this.customer_field.set_value(customer);
|
2019-06-17 10:49:23 +00:00
|
|
|
|
|
|
|
if (this.numpad) {
|
|
|
|
const disable_btns = this.disable_numpad_control()
|
|
|
|
const enable_btns = [__('Rate'), __('Disc')]
|
|
|
|
|
|
|
|
if (disable_btns) {
|
|
|
|
enable_btns.filter(btn => !disable_btns.includes(btn))
|
|
|
|
}
|
|
|
|
|
|
|
|
this.numpad.enable_buttons(enable_btns);
|
|
|
|
}
|
2017-08-24 11:39:34 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 12:21:32 +00:00
|
|
|
reset_pos_field_value() {
|
|
|
|
let value = '';
|
|
|
|
if (this.custom_pos_fields) {
|
|
|
|
this.custom_pos_fields.forEach(r => {
|
|
|
|
value = this.frm.doc[r.fieldname] || r.default_value || '';
|
|
|
|
|
|
|
|
if (this.fields) {
|
|
|
|
this.fields[r.fieldname].set_value(value);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
this.wrapper.find('.pos-fields').toggle(false);
|
|
|
|
this.wrapper.find('.pos-fields-octicon').toggle(true);
|
|
|
|
}
|
|
|
|
|
2017-08-27 08:26:33 +00:00
|
|
|
get_grand_total() {
|
2018-01-12 10:58:16 +00:00
|
|
|
let total = this.get_total_template('Grand Total', 'grand-total-value');
|
|
|
|
|
|
|
|
if (!cint(frappe.sys_defaults.disable_rounded_total)) {
|
|
|
|
total += this.get_total_template('Rounded Total', 'rounded-total-value');
|
|
|
|
}
|
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2018-02-08 10:50:46 +00:00
|
|
|
get_item_qty_total() {
|
|
|
|
let total = this.get_total_template('Total Qty', 'quantity-total');
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2018-01-12 10:58:16 +00:00
|
|
|
get_total_template(label, class_name) {
|
2017-08-27 08:26:33 +00:00
|
|
|
return `
|
|
|
|
<div class="list-item">
|
2018-01-12 10:58:16 +00:00
|
|
|
<div class="list-item__content text-muted">${__(label)}</div>
|
|
|
|
<div class="list-item__content list-item__content--flex-2 ${class_name}">0.00</div>
|
2017-08-27 08:26:33 +00:00
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
get_discount_amount() {
|
|
|
|
const get_currency_symbol = window.get_currency_symbol;
|
|
|
|
|
|
|
|
return `
|
|
|
|
<div class="list-item">
|
|
|
|
<div class="list-item__content list-item__content--flex-2 text-muted">${__('Discount')}</div>
|
|
|
|
<div class="list-item__content discount-inputs">
|
|
|
|
<input type="text"
|
2017-08-28 11:49:28 +00:00
|
|
|
class="form-control additional_discount_percentage text-right"
|
2017-08-27 08:26:33 +00:00
|
|
|
placeholder="% 0.00"
|
|
|
|
>
|
|
|
|
<input type="text"
|
2017-08-28 11:49:28 +00:00
|
|
|
class="form-control discount_amount text-right"
|
2017-08-27 08:26:33 +00:00
|
|
|
placeholder="${get_currency_symbol(this.frm.doc.currency)} 0.00"
|
|
|
|
>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
2017-08-24 11:39:34 +00:00
|
|
|
get_taxes_and_totals() {
|
|
|
|
return `
|
|
|
|
<div class="list-item">
|
|
|
|
<div class="list-item__content list-item__content--flex-2 text-muted">${__('Net Total')}</div>
|
|
|
|
<div class="list-item__content net-total">0.00</div>
|
|
|
|
</div>
|
|
|
|
<div class="list-item">
|
|
|
|
<div class="list-item__content list-item__content--flex-2 text-muted">${__('Taxes')}</div>
|
|
|
|
<div class="list-item__content taxes">0.00</div>
|
2017-08-27 08:26:33 +00:00
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
toggle_taxes_and_totals(flag) {
|
|
|
|
if (flag !== undefined) {
|
|
|
|
this.tax_area_is_shown = flag;
|
|
|
|
} else {
|
|
|
|
this.tax_area_is_shown = !this.tax_area_is_shown;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$taxes_and_totals.toggle(this.tax_area_is_shown);
|
|
|
|
this.$discount_amount.toggle(this.tax_area_is_shown);
|
|
|
|
}
|
|
|
|
|
|
|
|
update_taxes_and_totals() {
|
2017-09-26 07:57:06 +00:00
|
|
|
if (!this.frm.doc.taxes) { return; }
|
|
|
|
|
2017-08-27 08:26:33 +00:00
|
|
|
const currency = this.frm.doc.currency;
|
|
|
|
this.frm.refresh_field('taxes');
|
|
|
|
|
|
|
|
// Update totals
|
|
|
|
this.$taxes_and_totals.find('.net-total')
|
2017-10-06 05:46:46 +00:00
|
|
|
.html(format_currency(this.frm.doc.total, currency));
|
2017-08-27 08:26:33 +00:00
|
|
|
|
|
|
|
// Update taxes
|
|
|
|
const taxes_html = this.frm.doc.taxes.map(tax => {
|
|
|
|
return `
|
|
|
|
<div>
|
|
|
|
<span>${tax.description}</span>
|
|
|
|
<span class="text-right bold">
|
|
|
|
${format_currency(tax.tax_amount, currency)}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}).join("");
|
|
|
|
this.$taxes_and_totals.find('.taxes').html(taxes_html);
|
|
|
|
}
|
|
|
|
|
|
|
|
update_grand_total() {
|
|
|
|
this.$grand_total.find('.grand-total-value').text(
|
|
|
|
format_currency(this.frm.doc.grand_total, this.frm.currency)
|
|
|
|
);
|
2018-01-12 10:58:16 +00:00
|
|
|
|
|
|
|
this.$grand_total.find('.rounded-total-value').text(
|
|
|
|
format_currency(this.frm.doc.rounded_total, this.frm.currency)
|
|
|
|
);
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
2018-06-06 04:52:13 +00:00
|
|
|
update_qty_total() {
|
2018-02-08 10:50:46 +00:00
|
|
|
var total_item_qty = 0;
|
|
|
|
$.each(this.frm.doc["items"] || [], function (i, d) {
|
|
|
|
if (d.qty > 0) {
|
|
|
|
total_item_qty += d.qty;
|
|
|
|
}
|
|
|
|
});
|
2018-03-28 07:03:15 +00:00
|
|
|
this.$qty_total.find('.quantity-total').text(total_item_qty);
|
2018-02-08 10:50:46 +00:00
|
|
|
this.frm.set_value("pos_total_qty",total_item_qty);
|
|
|
|
}
|
|
|
|
|
2017-08-10 11:47:34 +00:00
|
|
|
make_customer_field() {
|
|
|
|
this.customer_field = frappe.ui.form.make_control({
|
|
|
|
df: {
|
|
|
|
fieldtype: 'Link',
|
|
|
|
label: 'Customer',
|
2017-08-31 05:46:14 +00:00
|
|
|
fieldname: 'customer',
|
2017-08-11 13:14:19 +00:00
|
|
|
options: 'Customer',
|
2017-08-14 12:55:59 +00:00
|
|
|
reqd: 1,
|
2017-12-19 05:51:00 +00:00
|
|
|
get_query: function() {
|
|
|
|
return {
|
|
|
|
query: 'erpnext.controllers.queries.customer_query'
|
|
|
|
}
|
|
|
|
},
|
2017-08-21 09:35:33 +00:00
|
|
|
onchange: () => {
|
2017-08-23 10:55:16 +00:00
|
|
|
this.events.on_customer_change(this.customer_field.get_value());
|
2018-07-06 07:06:57 +00:00
|
|
|
this.events.get_loyalty_details();
|
2017-08-11 13:14:19 +00:00
|
|
|
}
|
2017-08-10 11:47:34 +00:00
|
|
|
},
|
|
|
|
parent: this.wrapper.find('.customer-field'),
|
|
|
|
render_input: true
|
|
|
|
});
|
2017-10-02 06:43:36 +00:00
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
this.customer_field.set_value(this.frm.doc.customer);
|
2017-08-10 11:47:34 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 12:21:32 +00:00
|
|
|
make_pos_fields() {
|
|
|
|
const me = this;
|
|
|
|
|
|
|
|
this.fields = {};
|
|
|
|
this.wrapper.find('.pos-fields-octicon, .more-fields-section').click(() => {
|
|
|
|
this.wrapper.find('.pos-fields').toggle();
|
|
|
|
this.wrapper.find('.pos-fields-octicon').toggleClass('octicon-chevron-down').toggleClass('octicon-chevron-up');
|
|
|
|
});
|
|
|
|
this.wrapper.find('.pos-fields').toggle(false);
|
|
|
|
|
|
|
|
return new Promise(res => {
|
|
|
|
frappe.call({
|
|
|
|
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_pos_fields",
|
|
|
|
freeze: true,
|
|
|
|
}).then(r => {
|
|
|
|
if(r.message.length) {
|
|
|
|
this.wrapper.find('.pos-field-section').css('display','block');
|
|
|
|
this.custom_pos_fields = r.message;
|
|
|
|
if (r.message.length < 3) {
|
|
|
|
this.wrapper.find('.pos-fields').toggle(true);
|
|
|
|
this.wrapper.find('.pos-fields-octicon').toggleClass('octicon-chevron-down').toggleClass('octicon-chevron-up');
|
|
|
|
}
|
|
|
|
|
|
|
|
r.message.forEach(field => {
|
|
|
|
this.fields[field.fieldname] = frappe.ui.form.make_control({
|
|
|
|
df: {
|
|
|
|
fieldtype: field.fieldtype,
|
|
|
|
label: field.label,
|
|
|
|
fieldname: field.fieldname,
|
|
|
|
options: field.options,
|
|
|
|
reqd: field.reqd || 0,
|
|
|
|
read_only: field.read_only || 0,
|
|
|
|
default: field.default_value,
|
|
|
|
onchange: function() {
|
|
|
|
if (this.value) {
|
|
|
|
me.frm.set_value(this.df.fieldname, this.value);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
get_query: () => {
|
|
|
|
return this.get_query_for_pos_fields(field.fieldname)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
parent: this.wrapper.find('.pos-fields'),
|
|
|
|
render_input: true
|
|
|
|
});
|
|
|
|
|
|
|
|
if (this.frm.doc[field.fieldname]) {
|
|
|
|
this.fields[field.fieldname].set_value(this.frm.doc[field.fieldname]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
get_query_for_pos_fields(field) {
|
|
|
|
if (this.frm.fields_dict && this.frm.fields_dict[field]
|
|
|
|
&& this.frm.fields_dict[field].get_query) {
|
|
|
|
return this.frm.fields_dict[field].get_query(this.frm.doc);
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 07:06:57 +00:00
|
|
|
|
|
|
|
make_loyalty_points() {
|
|
|
|
this.available_loyalty_points = frappe.ui.form.make_control({
|
|
|
|
df: {
|
|
|
|
fieldtype: 'Int',
|
|
|
|
label: 'Available Loyalty Points',
|
|
|
|
read_only: 1,
|
|
|
|
fieldname: 'available_loyalty_points'
|
|
|
|
},
|
|
|
|
parent: this.wrapper.find('.loyalty-program-field')
|
|
|
|
});
|
|
|
|
this.available_loyalty_points.set_value(this.frm.doc.loyalty_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-02 06:52:58 +00:00
|
|
|
disable_numpad_control() {
|
|
|
|
let disabled_btns = [];
|
|
|
|
if(!this.frm.allow_edit_rate) {
|
2018-07-01 11:14:09 +00:00
|
|
|
disabled_btns.push(__('Rate'));
|
2018-02-01 12:26:24 +00:00
|
|
|
}
|
2018-02-02 06:52:58 +00:00
|
|
|
if(!this.frm.allow_edit_discount) {
|
2018-07-01 11:14:09 +00:00
|
|
|
disabled_btns.push(__('Disc'));
|
2018-02-01 12:26:24 +00:00
|
|
|
}
|
2018-02-02 06:52:58 +00:00
|
|
|
return disabled_btns;
|
2018-02-01 12:26:24 +00:00
|
|
|
}
|
|
|
|
|
2018-07-01 11:14:09 +00:00
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
make_numpad() {
|
2018-07-01 11:14:09 +00:00
|
|
|
|
|
|
|
var pay_class = {}
|
|
|
|
pay_class[__('Pay')]='brand-primary'
|
2017-08-14 12:55:59 +00:00
|
|
|
this.numpad = new NumberPad({
|
2017-08-23 07:42:12 +00:00
|
|
|
button_array: [
|
2018-07-01 11:14:09 +00:00
|
|
|
[1, 2, 3, Qty],
|
|
|
|
[4, 5, 6, Disc],
|
|
|
|
[7, 8, 9, Rate],
|
|
|
|
[Del, 0, '.', Pay]
|
2017-08-23 07:42:12 +00:00
|
|
|
],
|
2018-07-01 11:14:09 +00:00
|
|
|
add_class: pay_class,
|
|
|
|
disable_highlight: [Qty, Disc, Rate, Pay],
|
|
|
|
reset_btns: [Qty, Disc, Rate, Pay],
|
|
|
|
del_btn: Del,
|
2018-02-01 12:26:24 +00:00
|
|
|
disable_btns: this.disable_numpad_control(),
|
2017-08-14 12:55:59 +00:00
|
|
|
wrapper: this.wrapper.find('.number-pad-container'),
|
|
|
|
onclick: (btn_value) => {
|
|
|
|
// on click
|
2018-07-01 11:14:09 +00:00
|
|
|
|
|
|
|
if (!this.selected_item && btn_value !== Pay) {
|
2017-08-23 10:55:16 +00:00
|
|
|
frappe.show_alert({
|
|
|
|
indicator: 'red',
|
|
|
|
message: __('Please select an item in the cart')
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2018-07-01 11:14:09 +00:00
|
|
|
if ([Qty, Disc, Rate].includes(btn_value)) {
|
2017-08-23 10:55:16 +00:00
|
|
|
this.set_input_active(btn_value);
|
2018-07-01 11:14:09 +00:00
|
|
|
} else if (btn_value !== Pay) {
|
2017-08-23 10:55:16 +00:00
|
|
|
if (!this.selected_item.active_field) {
|
2017-08-23 07:42:12 +00:00
|
|
|
frappe.show_alert({
|
|
|
|
indicator: 'red',
|
2017-08-23 10:55:16 +00:00
|
|
|
message: __('Please select a field to edit from numpad')
|
2017-08-23 07:42:12 +00:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2017-08-23 10:55:16 +00:00
|
|
|
|
2018-02-07 18:52:53 +00:00
|
|
|
if (this.selected_item.active_field == 'discount_percentage' && this.numpad.get_value() > cint(100)) {
|
|
|
|
frappe.show_alert({
|
|
|
|
indicator: 'red',
|
|
|
|
message: __('Discount amount cannot be greater than 100%')
|
|
|
|
});
|
|
|
|
this.numpad.reset_value();
|
|
|
|
} else {
|
2018-04-19 07:25:06 +00:00
|
|
|
const item_code = unescape(this.selected_item.attr('data-item-code'));
|
2018-03-28 07:03:15 +00:00
|
|
|
const batch_no = this.selected_item.attr('data-batch-no');
|
2018-02-07 18:52:53 +00:00
|
|
|
const field = this.selected_item.active_field;
|
|
|
|
const value = this.numpad.get_value();
|
2017-08-23 10:55:16 +00:00
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
this.events.on_field_change(item_code, field, value, batch_no);
|
2018-02-07 18:52:53 +00:00
|
|
|
}
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
2017-08-23 07:42:12 +00:00
|
|
|
this.events.on_numpad(btn_value);
|
2017-08-14 12:55:59 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-21 09:35:33 +00:00
|
|
|
set_input_active(btn_value) {
|
2017-08-23 07:42:12 +00:00
|
|
|
this.selected_item.removeClass('qty disc rate');
|
2017-08-21 09:35:33 +00:00
|
|
|
|
2017-08-23 10:55:16 +00:00
|
|
|
this.numpad.set_active(btn_value);
|
2018-07-01 11:14:09 +00:00
|
|
|
if (btn_value === Qty) {
|
2017-08-23 07:42:12 +00:00
|
|
|
this.selected_item.addClass('qty');
|
2017-08-23 10:55:16 +00:00
|
|
|
this.selected_item.active_field = 'qty';
|
2018-07-01 11:14:09 +00:00
|
|
|
} else if (btn_value == Disc) {
|
2017-08-23 07:42:12 +00:00
|
|
|
this.selected_item.addClass('disc');
|
2017-08-23 10:55:16 +00:00
|
|
|
this.selected_item.active_field = 'discount_percentage';
|
2018-07-01 11:14:09 +00:00
|
|
|
} else if (btn_value == Rate) {
|
2017-08-23 07:42:12 +00:00
|
|
|
this.selected_item.addClass('rate');
|
2017-08-23 10:55:16 +00:00
|
|
|
this.selected_item.active_field = 'rate';
|
2017-08-21 09:35:33 +00:00
|
|
|
}
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
add_item(item) {
|
2017-08-24 11:39:34 +00:00
|
|
|
this.$empty_state.hide();
|
2017-08-10 05:52:03 +00:00
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
if (this.exists(item.item_code, item.batch_no)) {
|
2017-08-14 12:55:59 +00:00
|
|
|
// update quantity
|
|
|
|
this.update_item(item);
|
2017-12-14 10:36:23 +00:00
|
|
|
} else if (flt(item.qty) > 0.0) {
|
2017-08-14 12:55:59 +00:00
|
|
|
// add to cart
|
|
|
|
const $item = $(this.get_item_html(item));
|
|
|
|
$item.appendTo(this.$cart_items);
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
2017-08-14 12:55:59 +00:00
|
|
|
this.highlight_item(item.item_code);
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
update_item(item) {
|
2018-03-28 07:03:15 +00:00
|
|
|
const item_selector = item.batch_no ?
|
2018-04-24 05:46:13 +00:00
|
|
|
`[data-batch-no="${item.batch_no}"]` : `[data-item-code="${escape(item.item_code)}"]`;
|
2018-03-28 07:03:15 +00:00
|
|
|
|
|
|
|
const $item = this.$cart_items.find(item_selector);
|
2017-09-06 08:46:51 +00:00
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
if(item.qty > 0) {
|
2017-10-25 08:29:29 +00:00
|
|
|
const is_stock_item = this.get_item_details(item.item_code).is_stock_item;
|
|
|
|
const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red';
|
2017-09-06 08:46:51 +00:00
|
|
|
const remove_class = indicator_class == 'green' ? 'red' : 'green';
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
$item.find('.quantity input').val(item.qty);
|
2017-08-29 08:57:01 +00:00
|
|
|
$item.find('.discount').text(item.discount_percentage + '%');
|
2017-08-28 11:49:28 +00:00
|
|
|
$item.find('.rate').text(format_currency(item.rate, this.frm.doc.currency));
|
2017-09-06 08:46:51 +00:00
|
|
|
$item.addClass(indicator_class);
|
|
|
|
$item.removeClass(remove_class);
|
2017-08-14 12:55:59 +00:00
|
|
|
} else {
|
|
|
|
$item.remove();
|
|
|
|
}
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
2017-08-23 07:42:12 +00:00
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
get_item_html(item) {
|
2017-10-25 08:29:29 +00:00
|
|
|
const is_stock_item = this.get_item_details(item.item_code).is_stock_item;
|
2017-08-23 06:45:10 +00:00
|
|
|
const rate = format_currency(item.rate, this.frm.doc.currency);
|
2017-10-25 08:29:29 +00:00
|
|
|
const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red';
|
2018-04-04 12:36:19 +00:00
|
|
|
const batch_no = item.batch_no || '';
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
return `
|
2018-04-24 05:46:13 +00:00
|
|
|
<div class="list-item indicator ${indicator_class}" data-item-code="${escape(item.item_code)}"
|
2020-04-14 08:35:14 +00:00
|
|
|
data-batch-no="${batch_no}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty} ${item.stock_uom}">
|
2017-08-30 13:26:26 +00:00
|
|
|
<div class="item-name list-item__content list-item__content--flex-1.5 ellipsis">
|
2017-08-14 12:55:59 +00:00
|
|
|
${item.item_name}
|
2017-08-10 05:52:03 +00:00
|
|
|
</div>
|
|
|
|
<div class="quantity list-item__content text-right">
|
2017-08-14 12:55:59 +00:00
|
|
|
${get_quantity_html(item.qty)}
|
2017-08-10 05:52:03 +00:00
|
|
|
</div>
|
|
|
|
<div class="discount list-item__content text-right">
|
2017-08-14 12:55:59 +00:00
|
|
|
${item.discount_percentage}%
|
2017-08-10 05:52:03 +00:00
|
|
|
</div>
|
|
|
|
<div class="rate list-item__content text-right">
|
2017-08-23 06:45:10 +00:00
|
|
|
${rate}
|
2017-08-10 05:52:03 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`;
|
2017-08-14 12:55:59 +00:00
|
|
|
|
|
|
|
function get_quantity_html(value) {
|
|
|
|
return `
|
|
|
|
<div class="input-group input-group-xs">
|
|
|
|
<span class="input-group-btn">
|
|
|
|
<button class="btn btn-default btn-xs" data-action="increment">+</button>
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<input class="form-control" type="number" value="${value}">
|
|
|
|
|
|
|
|
<span class="input-group-btn">
|
|
|
|
<button class="btn btn-default btn-xs" data-action="decrement">-</button>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-25 08:29:29 +00:00
|
|
|
get_item_details(item_code) {
|
|
|
|
if (!this.item_data[item_code]) {
|
|
|
|
this.item_data[item_code] = this.events.get_item_details(item_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.item_data[item_code];
|
|
|
|
}
|
|
|
|
|
2018-03-28 07:03:15 +00:00
|
|
|
exists(item_code, batch_no) {
|
|
|
|
const is_exists = batch_no ?
|
2018-04-24 05:46:13 +00:00
|
|
|
`[data-batch-no="${batch_no}"]` : `[data-item-code="${escape(item_code)}"]`;
|
2018-03-28 07:03:15 +00:00
|
|
|
|
|
|
|
let $item = this.$cart_items.find(is_exists);
|
2018-04-24 05:46:13 +00:00
|
|
|
|
2017-08-23 06:45:10 +00:00
|
|
|
return $item.length > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
highlight_item(item_code) {
|
2018-04-19 07:25:06 +00:00
|
|
|
const $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
2017-08-23 06:45:10 +00:00
|
|
|
$item.addClass('highlight');
|
|
|
|
setTimeout(() => $item.removeClass('highlight'), 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
scroll_to_item(item_code) {
|
2018-04-19 07:25:06 +00:00
|
|
|
const $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
2017-08-23 10:55:16 +00:00
|
|
|
if ($item.length === 0) return;
|
2017-08-23 07:42:12 +00:00
|
|
|
const scrollTop = $item.offset().top - this.$cart_items.offset().top + this.$cart_items.scrollTop();
|
|
|
|
this.$cart_items.animate({ scrollTop });
|
2017-08-23 06:45:10 +00:00
|
|
|
}
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
bind_events() {
|
2017-08-23 07:42:12 +00:00
|
|
|
const me = this;
|
2017-08-14 12:55:59 +00:00
|
|
|
const events = this.events;
|
2017-08-23 07:42:12 +00:00
|
|
|
|
|
|
|
// quantity change
|
2017-08-14 12:55:59 +00:00
|
|
|
this.$cart_items.on('click',
|
|
|
|
'[data-action="increment"], [data-action="decrement"]', function() {
|
|
|
|
const $btn = $(this);
|
|
|
|
const $item = $btn.closest('.list-item[data-item-code]');
|
2018-04-19 07:25:06 +00:00
|
|
|
const item_code = unescape($item.attr('data-item-code'));
|
2017-08-14 12:55:59 +00:00
|
|
|
const action = $btn.attr('data-action');
|
|
|
|
|
|
|
|
if(action === 'increment') {
|
2017-08-23 10:55:16 +00:00
|
|
|
events.on_field_change(item_code, 'qty', '+1');
|
2017-08-14 12:55:59 +00:00
|
|
|
} else if(action === 'decrement') {
|
2017-08-23 10:55:16 +00:00
|
|
|
events.on_field_change(item_code, 'qty', '-1');
|
2017-08-14 12:55:59 +00:00
|
|
|
}
|
|
|
|
});
|
2017-08-23 07:42:12 +00:00
|
|
|
|
2017-08-23 10:55:16 +00:00
|
|
|
this.$cart_items.on('change', '.quantity input', function() {
|
|
|
|
const $input = $(this);
|
|
|
|
const $item = $input.closest('.list-item[data-item-code]');
|
2018-04-19 07:25:06 +00:00
|
|
|
const item_code = unescape($item.attr('data-item-code'));
|
2017-08-23 10:55:16 +00:00
|
|
|
events.on_field_change(item_code, 'qty', flt($input.val()));
|
|
|
|
});
|
|
|
|
|
2017-08-23 07:42:12 +00:00
|
|
|
// current item
|
2017-08-21 09:35:33 +00:00
|
|
|
this.$cart_items.on('click', '.list-item', function() {
|
2017-08-23 10:55:16 +00:00
|
|
|
me.set_selected_item($(this));
|
2017-08-21 09:35:33 +00:00
|
|
|
});
|
2017-08-23 07:42:12 +00:00
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
this.wrapper.find('.additional_discount_percentage').on('change', (e) => {
|
2017-10-04 09:03:12 +00:00
|
|
|
const discount_percentage = flt(e.target.value,
|
|
|
|
precision("additional_discount_percentage"));
|
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
frappe.model.set_value(this.frm.doctype, this.frm.docname,
|
2017-10-04 09:03:12 +00:00
|
|
|
'additional_discount_percentage', discount_percentage)
|
2017-08-28 11:49:28 +00:00
|
|
|
.then(() => {
|
2017-08-29 08:57:01 +00:00
|
|
|
let discount_wrapper = this.wrapper.find('.discount_amount');
|
2017-10-04 09:03:12 +00:00
|
|
|
discount_wrapper.val(flt(this.frm.doc.discount_amount,
|
|
|
|
precision('discount_amount')));
|
2017-08-29 08:57:01 +00:00
|
|
|
discount_wrapper.trigger('change');
|
|
|
|
});
|
|
|
|
});
|
2017-08-28 11:49:28 +00:00
|
|
|
|
|
|
|
this.wrapper.find('.discount_amount').on('change', (e) => {
|
2017-10-04 09:03:12 +00:00
|
|
|
const discount_amount = flt(e.target.value, precision('discount_amount'));
|
2017-08-28 11:49:28 +00:00
|
|
|
frappe.model.set_value(this.frm.doctype, this.frm.docname,
|
2017-10-04 09:03:12 +00:00
|
|
|
'discount_amount', discount_amount);
|
2017-08-28 11:49:28 +00:00
|
|
|
this.frm.trigger('discount_amount')
|
|
|
|
.then(() => {
|
2017-10-04 09:03:12 +00:00
|
|
|
this.update_discount_fields();
|
2017-08-29 08:57:01 +00:00
|
|
|
this.update_taxes_and_totals();
|
|
|
|
this.update_grand_total();
|
|
|
|
});
|
|
|
|
});
|
2017-08-23 07:42:12 +00:00
|
|
|
}
|
|
|
|
|
2017-10-04 09:03:12 +00:00
|
|
|
update_discount_fields() {
|
|
|
|
let discount_wrapper = this.wrapper.find('.additional_discount_percentage');
|
|
|
|
let discount_amt_wrapper = this.wrapper.find('.discount_amount');
|
|
|
|
discount_wrapper.val(flt(this.frm.doc.additional_discount_percentage,
|
|
|
|
precision('additional_discount_percentage')));
|
|
|
|
discount_amt_wrapper.val(flt(this.frm.doc.discount_amount,
|
|
|
|
precision('discount_amount')));
|
|
|
|
}
|
|
|
|
|
2017-08-23 10:55:16 +00:00
|
|
|
set_selected_item($item) {
|
|
|
|
this.selected_item = $item;
|
|
|
|
this.$cart_items.find('.list-item').removeClass('current-item qty disc rate');
|
|
|
|
this.selected_item.addClass('current-item');
|
|
|
|
this.events.on_select_change();
|
|
|
|
}
|
|
|
|
|
2017-08-23 07:42:12 +00:00
|
|
|
unselect_all() {
|
|
|
|
this.$cart_items.find('.list-item').removeClass('current-item qty disc rate');
|
|
|
|
this.selected_item = null;
|
|
|
|
this.events.on_select_change();
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
class POSItems {
|
2017-11-28 10:41:15 +00:00
|
|
|
constructor({wrapper, frm, events}) {
|
2017-08-10 05:52:03 +00:00
|
|
|
this.wrapper = wrapper;
|
2017-11-28 10:41:15 +00:00
|
|
|
this.frm = frm;
|
2017-08-10 05:52:03 +00:00
|
|
|
this.items = {};
|
2017-08-24 09:57:55 +00:00
|
|
|
this.events = events;
|
2017-11-28 10:41:15 +00:00
|
|
|
this.currency = this.frm.doc.currency;
|
2017-08-10 11:47:34 +00:00
|
|
|
|
2018-12-24 09:09:30 +00:00
|
|
|
frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name", (r) => {
|
|
|
|
this.parent_item_group = r.name;
|
|
|
|
this.make_dom();
|
|
|
|
this.make_fields();
|
2017-08-10 05:52:03 +00:00
|
|
|
|
2018-12-24 09:09:30 +00:00
|
|
|
this.init_clusterize();
|
|
|
|
this.bind_events();
|
|
|
|
this.load_items_data();
|
|
|
|
})
|
2017-11-28 10:41:15 +00:00
|
|
|
}
|
2017-08-10 05:52:03 +00:00
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
load_items_data() {
|
2017-08-10 05:52:03 +00:00
|
|
|
// bootstrap with 20 items
|
|
|
|
this.get_items()
|
2017-08-29 09:57:17 +00:00
|
|
|
.then(({ items }) => {
|
|
|
|
this.all_items = items;
|
2017-08-23 10:55:16 +00:00
|
|
|
this.items = items;
|
2017-08-29 09:57:17 +00:00
|
|
|
this.render_items(items);
|
|
|
|
});
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
2017-11-28 10:41:15 +00:00
|
|
|
reset_items() {
|
|
|
|
this.wrapper.find('.pos-items').empty();
|
|
|
|
this.init_clusterize();
|
|
|
|
this.load_items_data();
|
|
|
|
}
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
make_dom() {
|
|
|
|
this.wrapper.html(`
|
|
|
|
<div class="fields">
|
|
|
|
<div class="search-field">
|
|
|
|
</div>
|
|
|
|
<div class="item-group-field">
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="items-wrapper">
|
|
|
|
</div>
|
|
|
|
`);
|
|
|
|
|
|
|
|
this.items_wrapper = this.wrapper.find('.items-wrapper');
|
|
|
|
this.items_wrapper.append(`
|
|
|
|
<div class="list-item-table pos-items-wrapper">
|
|
|
|
<div class="pos-items image-view-container">
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
|
|
|
|
make_fields() {
|
2017-08-10 12:58:05 +00:00
|
|
|
// Search field
|
2018-12-24 09:09:30 +00:00
|
|
|
const me = this;
|
2017-08-10 05:52:03 +00:00
|
|
|
this.search_field = frappe.ui.form.make_control({
|
|
|
|
df: {
|
|
|
|
fieldtype: 'Data',
|
2018-07-01 11:14:09 +00:00
|
|
|
label: __('Search Item (Ctrl + i)'),
|
|
|
|
placeholder: __('Search by item code, serial number, batch no or barcode')
|
2017-08-10 05:52:03 +00:00
|
|
|
},
|
|
|
|
parent: this.wrapper.find('.search-field'),
|
|
|
|
render_input: true,
|
|
|
|
});
|
|
|
|
|
2017-08-10 12:58:05 +00:00
|
|
|
frappe.ui.keys.on('ctrl+i', () => {
|
|
|
|
this.search_field.set_focus();
|
|
|
|
});
|
|
|
|
|
|
|
|
this.search_field.$input.on('input', (e) => {
|
2017-08-29 09:57:17 +00:00
|
|
|
clearTimeout(this.last_search);
|
|
|
|
this.last_search = setTimeout(() => {
|
|
|
|
const search_term = e.target.value;
|
2019-04-04 10:32:15 +00:00
|
|
|
const item_group = this.item_group_field ?
|
|
|
|
this.item_group_field.get_value() : '';
|
|
|
|
|
|
|
|
this.filter_items({ search_term:search_term, item_group: item_group});
|
2017-08-29 09:57:17 +00:00
|
|
|
}, 300);
|
2017-08-10 12:58:05 +00:00
|
|
|
});
|
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
this.item_group_field = frappe.ui.form.make_control({
|
|
|
|
df: {
|
2017-08-28 11:49:28 +00:00
|
|
|
fieldtype: 'Link',
|
2017-08-10 05:52:03 +00:00
|
|
|
label: 'Item Group',
|
2017-08-28 11:49:28 +00:00
|
|
|
options: 'Item Group',
|
2018-12-24 09:09:30 +00:00
|
|
|
default: me.parent_item_group,
|
2017-08-28 11:49:28 +00:00
|
|
|
onchange: () => {
|
2017-08-30 13:26:26 +00:00
|
|
|
const item_group = this.item_group_field.get_value();
|
2017-08-29 13:47:02 +00:00
|
|
|
if (item_group) {
|
2017-08-30 13:26:26 +00:00
|
|
|
this.filter_items({ item_group: item_group });
|
2017-08-29 13:47:02 +00:00
|
|
|
}
|
2017-08-28 11:49:28 +00:00
|
|
|
},
|
2017-11-29 08:25:13 +00:00
|
|
|
get_query: () => {
|
|
|
|
return {
|
|
|
|
query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
|
|
|
|
filters: {
|
|
|
|
pos_profile: this.frm.doc.pos_profile
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2017-08-10 05:52:03 +00:00
|
|
|
},
|
|
|
|
parent: this.wrapper.find('.item-group-field'),
|
|
|
|
render_input: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
init_clusterize() {
|
|
|
|
this.clusterize = new Clusterize({
|
|
|
|
scrollElem: this.wrapper.find('.pos-items-wrapper')[0],
|
|
|
|
contentElem: this.wrapper.find('.pos-items')[0],
|
|
|
|
rows_in_block: 6
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
render_items(items) {
|
|
|
|
let _items = items || this.items;
|
|
|
|
|
|
|
|
const all_items = Object.values(_items).map(item => this.get_item_html(item));
|
|
|
|
let row_items = [];
|
|
|
|
|
2017-10-17 07:00:34 +00:00
|
|
|
const row_container = '<div class="image-view-row">';
|
2017-08-10 05:52:03 +00:00
|
|
|
let curr_row = row_container;
|
2017-08-23 10:55:16 +00:00
|
|
|
|
2017-08-10 05:52:03 +00:00
|
|
|
for (let i=0; i < all_items.length; i++) {
|
|
|
|
// wrap 4 items in a div to emulate
|
|
|
|
// a row for clusterize
|
|
|
|
if(i % 4 === 0 && i !== 0) {
|
|
|
|
curr_row += '</div>';
|
|
|
|
row_items.push(curr_row);
|
|
|
|
curr_row = row_container;
|
|
|
|
}
|
|
|
|
curr_row += all_items[i];
|
2017-08-23 10:55:16 +00:00
|
|
|
|
2017-12-15 10:08:12 +00:00
|
|
|
if(i == all_items.length - 1) {
|
2017-08-23 10:55:16 +00:00
|
|
|
row_items.push(curr_row);
|
|
|
|
}
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.clusterize.update(row_items);
|
|
|
|
}
|
|
|
|
|
2018-12-24 09:09:30 +00:00
|
|
|
filter_items({ search_term='', item_group=this.parent_item_group }={}) {
|
2017-08-28 11:49:28 +00:00
|
|
|
if (search_term) {
|
|
|
|
search_term = search_term.toLowerCase();
|
2017-08-27 08:34:23 +00:00
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
// memoize
|
|
|
|
this.search_index = this.search_index || {};
|
|
|
|
if (this.search_index[search_term]) {
|
|
|
|
const items = this.search_index[search_term];
|
2017-10-25 08:29:29 +00:00
|
|
|
this.items = items;
|
2017-08-28 11:49:28 +00:00
|
|
|
this.render_items(items);
|
2017-10-05 13:00:51 +00:00
|
|
|
this.set_item_in_the_cart(items);
|
2017-08-28 11:49:28 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-12-24 09:09:30 +00:00
|
|
|
} else if (item_group == this.parent_item_group) {
|
2017-10-25 08:29:29 +00:00
|
|
|
this.items = this.all_items;
|
2017-08-29 09:57:17 +00:00
|
|
|
return this.render_items(this.all_items);
|
2017-08-27 08:34:23 +00:00
|
|
|
}
|
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
this.get_items({search_value: search_term, item_group })
|
2017-11-10 09:59:14 +00:00
|
|
|
.then(({ items, serial_no, batch_no, barcode }) => {
|
2017-11-17 08:19:12 +00:00
|
|
|
if (search_term && !barcode) {
|
2017-08-28 11:49:28 +00:00
|
|
|
this.search_index[search_term] = items;
|
|
|
|
}
|
2017-08-27 08:34:23 +00:00
|
|
|
|
2017-10-25 08:29:29 +00:00
|
|
|
this.items = items;
|
2017-08-23 10:55:16 +00:00
|
|
|
this.render_items(items);
|
2017-11-10 09:59:14 +00:00
|
|
|
this.set_item_in_the_cart(items, serial_no, batch_no, barcode);
|
2017-08-23 10:55:16 +00:00
|
|
|
});
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
2017-11-10 09:59:14 +00:00
|
|
|
set_item_in_the_cart(items, serial_no, batch_no, barcode) {
|
2017-10-05 13:00:51 +00:00
|
|
|
if (serial_no) {
|
|
|
|
this.events.update_cart(items[0].item_code,
|
|
|
|
'serial_no', serial_no);
|
|
|
|
this.reset_search_field();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (batch_no) {
|
|
|
|
this.events.update_cart(items[0].item_code,
|
|
|
|
'batch_no', batch_no);
|
|
|
|
this.reset_search_field();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-10 09:59:14 +00:00
|
|
|
if (items.length === 1 && (serial_no || batch_no || barcode)) {
|
2017-10-05 13:00:51 +00:00
|
|
|
this.events.update_cart(items[0].item_code,
|
|
|
|
'qty', '+1');
|
|
|
|
this.reset_search_field();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-04 09:03:12 +00:00
|
|
|
reset_search_field() {
|
|
|
|
this.search_field.set_value('');
|
|
|
|
this.search_field.$input.trigger("input");
|
|
|
|
}
|
|
|
|
|
2017-08-24 09:57:55 +00:00
|
|
|
bind_events() {
|
|
|
|
var me = this;
|
2017-08-30 13:26:26 +00:00
|
|
|
this.wrapper.on('click', '.pos-item-wrapper', function() {
|
2017-08-10 05:52:03 +00:00
|
|
|
const $item = $(this);
|
2018-04-19 07:25:06 +00:00
|
|
|
const item_code = unescape($item.attr('data-item-code'));
|
2017-08-29 09:57:17 +00:00
|
|
|
me.events.update_cart(item_code, 'qty', '+1');
|
2017-08-10 05:52:03 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
get(item_code) {
|
2017-10-25 08:29:29 +00:00
|
|
|
let item = {};
|
|
|
|
this.items.map(data => {
|
|
|
|
if (data.item_code === item_code) {
|
|
|
|
item = data;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return item
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get_all() {
|
|
|
|
return this.items;
|
|
|
|
}
|
|
|
|
|
|
|
|
get_item_html(item) {
|
2017-08-23 10:55:16 +00:00
|
|
|
const price_list_rate = format_currency(item.price_list_rate, this.currency);
|
2017-09-06 08:46:51 +00:00
|
|
|
const { item_code, item_name, item_image} = item;
|
2017-08-10 05:52:03 +00:00
|
|
|
const item_title = item_name || item_code;
|
|
|
|
|
|
|
|
const template = `
|
2018-04-19 07:25:06 +00:00
|
|
|
<div class="pos-item-wrapper image-view-item" data-item-code="${escape(item_code)}">
|
2017-08-10 05:52:03 +00:00
|
|
|
<div class="image-view-header">
|
|
|
|
<div>
|
|
|
|
<a class="grey list-id" data-name="${item_code}" title="${item_title}">
|
|
|
|
${item_title}
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="image-view-body">
|
|
|
|
<a data-item-code="${item_code}"
|
|
|
|
title="${item_title}"
|
|
|
|
>
|
|
|
|
<div class="image-field"
|
|
|
|
style="${!item_image ? 'background-color: #fafbfc;' : ''} border: 0px;"
|
|
|
|
>
|
2017-08-14 12:55:59 +00:00
|
|
|
${!item_image ? `<span class="placeholder-text">
|
2017-08-10 05:52:03 +00:00
|
|
|
${frappe.get_abbr(item_title)}
|
2017-08-14 12:55:59 +00:00
|
|
|
</span>` : '' }
|
|
|
|
${item_image ? `<img src="${item_image}" alt="${item_title}">` : '' }
|
2017-08-10 05:52:03 +00:00
|
|
|
</div>
|
|
|
|
<span class="price-info">
|
2017-08-23 06:45:10 +00:00
|
|
|
${price_list_rate}
|
2017-08-10 05:52:03 +00:00
|
|
|
</span>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
|
|
|
|
return template;
|
|
|
|
}
|
|
|
|
|
2018-12-24 09:09:30 +00:00
|
|
|
get_items({start = 0, page_length = 40, search_value='', item_group=this.parent_item_group}={}) {
|
2019-01-25 11:11:01 +00:00
|
|
|
const price_list = this.frm.doc.selling_price_list;
|
2017-08-10 05:52:03 +00:00
|
|
|
return new Promise(res => {
|
|
|
|
frappe.call({
|
2017-08-23 06:45:10 +00:00
|
|
|
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
|
2018-01-05 06:56:33 +00:00
|
|
|
freeze: true,
|
2017-08-10 05:52:03 +00:00
|
|
|
args: {
|
2017-08-23 10:55:16 +00:00
|
|
|
start,
|
|
|
|
page_length,
|
2019-01-25 11:11:01 +00:00
|
|
|
price_list,
|
2017-08-28 11:49:28 +00:00
|
|
|
item_group,
|
2017-11-29 08:25:13 +00:00
|
|
|
search_value,
|
2019-01-25 11:11:01 +00:00
|
|
|
pos_profile: this.frm.doc.pos_profile
|
2017-08-10 05:52:03 +00:00
|
|
|
}
|
2017-08-14 12:55:59 +00:00
|
|
|
}).then(r => {
|
2017-08-29 09:57:17 +00:00
|
|
|
// const { items, serial_no, batch_no } = r.message;
|
2017-08-10 05:52:03 +00:00
|
|
|
|
2017-08-29 09:57:17 +00:00
|
|
|
// this.serial_no = serial_no || "";
|
|
|
|
res(r.message);
|
2017-08-10 05:52:03 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2017-08-14 12:55:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class NumberPad {
|
2017-08-23 10:55:16 +00:00
|
|
|
constructor({
|
|
|
|
wrapper, onclick, button_array,
|
|
|
|
add_class={}, disable_highlight=[],
|
2018-01-30 05:09:46 +00:00
|
|
|
reset_btns=[], del_btn='', disable_btns
|
2017-08-23 10:55:16 +00:00
|
|
|
}) {
|
2017-08-14 12:55:59 +00:00
|
|
|
this.wrapper = wrapper;
|
|
|
|
this.onclick = onclick;
|
2017-08-21 09:35:33 +00:00
|
|
|
this.button_array = button_array;
|
2017-08-23 07:42:12 +00:00
|
|
|
this.add_class = add_class;
|
|
|
|
this.disable_highlight = disable_highlight;
|
2017-08-23 10:55:16 +00:00
|
|
|
this.reset_btns = reset_btns;
|
|
|
|
this.del_btn = del_btn;
|
2018-02-01 11:48:38 +00:00
|
|
|
this.disable_btns = disable_btns || [];
|
2017-08-14 12:55:59 +00:00
|
|
|
this.make_dom();
|
|
|
|
this.bind_events();
|
2017-08-23 10:55:16 +00:00
|
|
|
this.value = '';
|
2017-08-14 12:55:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
make_dom() {
|
2017-08-21 09:35:33 +00:00
|
|
|
if (!this.button_array) {
|
|
|
|
this.button_array = [
|
2017-08-23 07:42:12 +00:00
|
|
|
[1, 2, 3],
|
|
|
|
[4, 5, 6],
|
|
|
|
[7, 8, 9],
|
|
|
|
['', 0, '']
|
2017-08-21 09:35:33 +00:00
|
|
|
];
|
|
|
|
}
|
2017-08-14 12:55:59 +00:00
|
|
|
|
|
|
|
this.wrapper.html(`
|
|
|
|
<div class="number-pad">
|
2017-08-21 09:35:33 +00:00
|
|
|
${this.button_array.map(get_row).join("")}
|
2017-08-14 12:55:59 +00:00
|
|
|
</div>
|
|
|
|
`);
|
|
|
|
|
|
|
|
function get_row(row) {
|
|
|
|
return '<div class="num-row">' + row.map(get_col).join("") + '</div>';
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_col(col) {
|
|
|
|
return `<div class="num-col" data-value="${col}"><div>${col}</div></div>`;
|
|
|
|
}
|
2017-08-23 07:42:12 +00:00
|
|
|
|
|
|
|
this.set_class();
|
2018-01-30 05:09:46 +00:00
|
|
|
|
2018-02-02 05:39:20 +00:00
|
|
|
if(this.disable_btns) {
|
|
|
|
this.disable_btns.forEach((btn) => {
|
|
|
|
const $btn = this.get_btn(btn);
|
|
|
|
$btn.prop("disabled", true)
|
|
|
|
$btn.hover(() => {
|
|
|
|
$btn.css('cursor','not-allowed');
|
|
|
|
})
|
2018-01-30 05:09:46 +00:00
|
|
|
})
|
2018-02-02 05:39:20 +00:00
|
|
|
}
|
2017-08-23 07:42:12 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 10:49:23 +00:00
|
|
|
enable_buttons(btns) {
|
|
|
|
btns.forEach((btn) => {
|
|
|
|
const $btn = this.get_btn(btn);
|
|
|
|
$btn.prop("disabled", false)
|
|
|
|
$btn.hover(() => {
|
|
|
|
$btn.css('cursor','pointer');
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-23 07:42:12 +00:00
|
|
|
set_class() {
|
|
|
|
for (const btn in this.add_class) {
|
|
|
|
const class_name = this.add_class[btn];
|
|
|
|
this.get_btn(btn).addClass(class_name);
|
|
|
|
}
|
2017-08-14 12:55:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bind_events() {
|
|
|
|
// bind click event
|
|
|
|
const me = this;
|
|
|
|
this.wrapper.on('click', '.num-col', function() {
|
|
|
|
const $btn = $(this);
|
2017-08-23 07:42:12 +00:00
|
|
|
const btn_value = $btn.attr('data-value');
|
|
|
|
if (!me.disable_highlight.includes(btn_value)) {
|
|
|
|
me.highlight_button($btn);
|
|
|
|
}
|
2017-08-23 10:55:16 +00:00
|
|
|
if (me.reset_btns.includes(btn_value)) {
|
2017-08-24 11:39:34 +00:00
|
|
|
me.reset_value();
|
2017-08-23 10:55:16 +00:00
|
|
|
} else {
|
|
|
|
if (btn_value === me.del_btn) {
|
|
|
|
me.value = me.value.substr(0, me.value.length - 1);
|
|
|
|
} else {
|
|
|
|
me.value += btn_value;
|
|
|
|
}
|
|
|
|
}
|
2017-08-23 07:42:12 +00:00
|
|
|
me.onclick(btn_value);
|
2017-08-14 12:55:59 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-24 11:39:34 +00:00
|
|
|
reset_value() {
|
|
|
|
this.value = '';
|
|
|
|
}
|
|
|
|
|
2017-08-23 10:55:16 +00:00
|
|
|
get_value() {
|
|
|
|
return flt(this.value);
|
|
|
|
}
|
|
|
|
|
2017-08-23 07:42:12 +00:00
|
|
|
get_btn(btn_value) {
|
|
|
|
return this.wrapper.find(`.num-col[data-value="${btn_value}"]`);
|
|
|
|
}
|
|
|
|
|
2017-08-14 12:55:59 +00:00
|
|
|
highlight_button($btn) {
|
|
|
|
$btn.addClass('highlight');
|
|
|
|
setTimeout(() => $btn.removeClass('highlight'), 1000);
|
|
|
|
}
|
2017-08-23 07:42:12 +00:00
|
|
|
|
|
|
|
set_active(btn_value) {
|
|
|
|
const $btn = this.get_btn(btn_value);
|
|
|
|
this.wrapper.find('.num-col').removeClass('active');
|
|
|
|
$btn.addClass('active');
|
|
|
|
}
|
|
|
|
|
|
|
|
set_inactive() {
|
|
|
|
this.wrapper.find('.num-col').removeClass('active');
|
|
|
|
}
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
2017-08-21 09:35:33 +00:00
|
|
|
class Payment {
|
2017-08-24 09:57:55 +00:00
|
|
|
constructor({frm, events}) {
|
2017-08-21 09:35:33 +00:00
|
|
|
this.frm = frm;
|
2017-08-24 09:57:55 +00:00
|
|
|
this.events = events;
|
2017-08-18 06:45:19 +00:00
|
|
|
this.make();
|
2017-08-27 03:24:40 +00:00
|
|
|
this.bind_events();
|
2017-08-18 06:45:19 +00:00
|
|
|
this.set_primary_action();
|
|
|
|
}
|
|
|
|
|
2017-08-21 09:35:33 +00:00
|
|
|
open_modal() {
|
|
|
|
this.dialog.show();
|
|
|
|
}
|
|
|
|
|
2017-08-18 06:45:19 +00:00
|
|
|
make() {
|
2017-08-21 09:35:33 +00:00
|
|
|
this.set_flag();
|
2017-08-18 06:45:19 +00:00
|
|
|
this.dialog = new frappe.ui.Dialog({
|
|
|
|
fields: this.get_fields(),
|
2018-07-06 07:06:57 +00:00
|
|
|
width: 800,
|
|
|
|
invoice_frm: this.frm
|
2017-08-18 06:45:19 +00:00
|
|
|
});
|
|
|
|
|
2017-10-11 06:33:49 +00:00
|
|
|
this.set_title();
|
|
|
|
|
2017-08-18 06:45:19 +00:00
|
|
|
this.$body = this.dialog.body;
|
|
|
|
|
|
|
|
this.numpad = new NumberPad({
|
2017-08-21 09:35:33 +00:00
|
|
|
wrapper: $(this.$body).find('[data-fieldname="numpad"]'),
|
|
|
|
button_array: [
|
|
|
|
[1, 2, 3],
|
|
|
|
[4, 5, 6],
|
|
|
|
[7, 8, 9],
|
2018-07-01 11:14:09 +00:00
|
|
|
[__('Del'), 0, '.'],
|
2017-08-21 09:35:33 +00:00
|
|
|
],
|
2017-08-27 08:26:33 +00:00
|
|
|
onclick: () => {
|
2017-08-27 03:24:40 +00:00
|
|
|
if(this.fieldname) {
|
2017-08-27 08:26:33 +00:00
|
|
|
this.dialog.set_value(this.fieldname, this.numpad.get_value());
|
2017-08-27 03:24:40 +00:00
|
|
|
}
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-10-10 20:24:36 +00:00
|
|
|
set_title() {
|
|
|
|
let title = __('Total Amount {0}',
|
2018-01-12 10:58:16 +00:00
|
|
|
[format_currency(this.frm.doc.rounded_total || this.frm.doc.grand_total,
|
|
|
|
this.frm.doc.currency)]);
|
2017-10-10 20:24:36 +00:00
|
|
|
|
|
|
|
this.dialog.set_title(title);
|
|
|
|
}
|
|
|
|
|
2017-08-27 03:24:40 +00:00
|
|
|
bind_events() {
|
|
|
|
var me = this;
|
|
|
|
$(this.dialog.body).find('.input-with-feedback').focusin(function() {
|
2017-08-27 08:26:33 +00:00
|
|
|
me.numpad.reset_value();
|
2017-08-27 03:24:40 +00:00
|
|
|
me.fieldname = $(this).prop('dataset').fieldname;
|
2017-11-18 11:28:58 +00:00
|
|
|
if (me.frm.doc.outstanding_amount > 0 &&
|
|
|
|
!in_list(['write_off_amount', 'change_amount'], me.fieldname)) {
|
|
|
|
me.frm.doc.payments.forEach((data) => {
|
|
|
|
if (data.mode_of_payment == me.fieldname && !data.amount) {
|
|
|
|
me.dialog.set_value(me.fieldname,
|
|
|
|
me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2017-08-27 08:26:33 +00:00
|
|
|
});
|
2017-08-27 03:24:40 +00:00
|
|
|
}
|
|
|
|
|
2017-08-18 06:45:19 +00:00
|
|
|
set_primary_action() {
|
2017-08-23 06:45:10 +00:00
|
|
|
var me = this;
|
|
|
|
|
2017-08-18 06:45:19 +00:00
|
|
|
this.dialog.set_primary_action(__("Submit"), function() {
|
2017-08-24 11:39:34 +00:00
|
|
|
me.dialog.hide();
|
|
|
|
me.events.submit_form();
|
|
|
|
});
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get_fields() {
|
|
|
|
const me = this;
|
|
|
|
|
2017-08-27 03:24:40 +00:00
|
|
|
let fields = this.frm.doc.payments.map(p => {
|
2017-08-18 06:45:19 +00:00
|
|
|
return {
|
|
|
|
fieldtype: 'Currency',
|
|
|
|
label: __(p.mode_of_payment),
|
|
|
|
options: me.frm.doc.currency,
|
|
|
|
fieldname: p.mode_of_payment,
|
|
|
|
default: p.amount,
|
2017-08-30 13:26:26 +00:00
|
|
|
onchange: () => {
|
2018-02-26 07:34:26 +00:00
|
|
|
const value = this.dialog.get_value(this.fieldname) || 0;
|
2017-08-27 03:24:40 +00:00
|
|
|
me.update_payment_value(this.fieldname, value);
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
2017-08-21 09:35:33 +00:00
|
|
|
};
|
2017-08-27 03:24:40 +00:00
|
|
|
});
|
2017-08-18 06:45:19 +00:00
|
|
|
|
2017-08-21 09:35:33 +00:00
|
|
|
fields = fields.concat([
|
2017-08-18 06:45:19 +00:00
|
|
|
{
|
|
|
|
fieldtype: 'Column Break',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'HTML',
|
|
|
|
fieldname: 'numpad'
|
|
|
|
},
|
2018-07-06 07:06:57 +00:00
|
|
|
{
|
|
|
|
fieldtype: 'Section Break',
|
|
|
|
depends_on: 'eval: this.invoice_frm.doc.loyalty_program'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Check',
|
|
|
|
label: 'Redeem Loyalty Points',
|
|
|
|
fieldname: 'redeem_loyalty_points',
|
|
|
|
onchange: () => {
|
|
|
|
me.update_cur_frm_value("redeem_loyalty_points", () => {
|
|
|
|
frappe.flags.redeem_loyalty_points = false;
|
|
|
|
me.update_loyalty_points();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Column Break',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Int',
|
|
|
|
fieldname: "loyalty_points",
|
|
|
|
label: __("Loyalty Points"),
|
|
|
|
depends_on: "redeem_loyalty_points",
|
|
|
|
onchange: () => {
|
|
|
|
me.update_cur_frm_value("loyalty_points", () => {
|
|
|
|
frappe.flags.loyalty_points = false;
|
|
|
|
me.update_loyalty_points();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Currency',
|
|
|
|
label: __("Loyalty Amount"),
|
|
|
|
fieldname: "loyalty_amount",
|
|
|
|
options: me.frm.doc.currency,
|
|
|
|
read_only: 1,
|
|
|
|
depends_on: "redeem_loyalty_points"
|
|
|
|
},
|
2017-08-18 06:45:19 +00:00
|
|
|
{
|
|
|
|
fieldtype: 'Section Break',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Currency',
|
|
|
|
label: __("Write off Amount"),
|
|
|
|
options: me.frm.doc.currency,
|
|
|
|
fieldname: "write_off_amount",
|
|
|
|
default: me.frm.doc.write_off_amount,
|
2017-08-21 09:35:33 +00:00
|
|
|
onchange: () => {
|
2017-08-18 06:45:19 +00:00
|
|
|
me.update_cur_frm_value('write_off_amount', () => {
|
|
|
|
frappe.flags.change_amount = false;
|
2017-08-30 13:26:26 +00:00
|
|
|
me.update_change_amount();
|
2017-08-21 09:35:33 +00:00
|
|
|
});
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Column Break',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Currency',
|
|
|
|
label: __("Change Amount"),
|
|
|
|
options: me.frm.doc.currency,
|
|
|
|
fieldname: "change_amount",
|
|
|
|
default: me.frm.doc.change_amount,
|
2017-08-21 09:35:33 +00:00
|
|
|
onchange: () => {
|
2017-08-18 06:45:19 +00:00
|
|
|
me.update_cur_frm_value('change_amount', () => {
|
|
|
|
frappe.flags.write_off_amount = false;
|
2017-08-21 09:35:33 +00:00
|
|
|
me.update_write_off_amount();
|
|
|
|
});
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
},
|
2017-08-27 03:24:40 +00:00
|
|
|
{
|
|
|
|
fieldtype: 'Section Break',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Currency',
|
|
|
|
label: __("Paid Amount"),
|
|
|
|
options: me.frm.doc.currency,
|
|
|
|
fieldname: "paid_amount",
|
|
|
|
default: me.frm.doc.paid_amount,
|
|
|
|
read_only: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Column Break',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Currency',
|
|
|
|
label: __("Outstanding Amount"),
|
|
|
|
options: me.frm.doc.currency,
|
|
|
|
fieldname: "outstanding_amount",
|
|
|
|
default: me.frm.doc.outstanding_amount,
|
|
|
|
read_only: 1
|
|
|
|
},
|
2017-08-21 09:35:33 +00:00
|
|
|
]);
|
2017-08-18 06:45:19 +00:00
|
|
|
|
2017-08-21 09:35:33 +00:00
|
|
|
return fields;
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
2017-08-21 09:35:33 +00:00
|
|
|
set_flag() {
|
2017-08-18 06:45:19 +00:00
|
|
|
frappe.flags.write_off_amount = true;
|
|
|
|
frappe.flags.change_amount = true;
|
2018-07-06 07:06:57 +00:00
|
|
|
frappe.flags.loyalty_points = true;
|
|
|
|
frappe.flags.redeem_loyalty_points = true;
|
|
|
|
frappe.flags.payment_method = true;
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
update_cur_frm_value(fieldname, callback) {
|
|
|
|
if (frappe.flags[fieldname]) {
|
|
|
|
const value = this.dialog.get_value(fieldname);
|
|
|
|
this.frm.set_value(fieldname, value)
|
2017-08-18 11:30:02 +00:00
|
|
|
.then(() => {
|
2017-08-30 13:26:26 +00:00
|
|
|
callback();
|
|
|
|
});
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
frappe.flags[fieldname] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
update_payment_value(fieldname, value) {
|
|
|
|
var me = this;
|
2018-07-06 07:06:57 +00:00
|
|
|
$.each(this.frm.doc.payments, function(i, data) {
|
|
|
|
if (__(data.mode_of_payment) == __(fieldname)) {
|
|
|
|
frappe.model.set_value('Sales Invoice Payment', data.name, 'amount', value)
|
|
|
|
.then(() => {
|
|
|
|
me.update_change_amount();
|
|
|
|
me.update_write_off_amount();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
update_change_amount() {
|
2017-08-30 13:26:26 +00:00
|
|
|
this.dialog.set_value("change_amount", this.frm.doc.change_amount);
|
|
|
|
this.show_paid_amount();
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
update_write_off_amount() {
|
2017-08-30 13:26:26 +00:00
|
|
|
this.dialog.set_value("write_off_amount", this.frm.doc.write_off_amount);
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
|
|
|
|
2017-08-27 03:24:40 +00:00
|
|
|
show_paid_amount() {
|
2017-08-30 13:26:26 +00:00
|
|
|
this.dialog.set_value("paid_amount", this.frm.doc.paid_amount);
|
|
|
|
this.dialog.set_value("outstanding_amount", this.frm.doc.outstanding_amount);
|
2017-08-18 06:45:19 +00:00
|
|
|
}
|
2018-07-06 07:06:57 +00:00
|
|
|
|
|
|
|
update_payment_amount() {
|
|
|
|
var me = this;
|
|
|
|
$.each(this.frm.doc.payments, function(i, data) {
|
|
|
|
console.log("setting the ", data.mode_of_payment, " for the value", data.amount);
|
|
|
|
me.dialog.set_value(data.mode_of_payment, data.amount);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
update_loyalty_points() {
|
|
|
|
if (this.dialog.get_value("redeem_loyalty_points")) {
|
|
|
|
this.dialog.set_value("loyalty_points", this.frm.doc.loyalty_points);
|
|
|
|
this.dialog.set_value("loyalty_amount", this.frm.doc.loyalty_amount);
|
|
|
|
this.update_payment_amount();
|
|
|
|
this.show_paid_amount();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-06 04:52:13 +00:00
|
|
|
}
|