[Enhancement] POS Redesign (#7639)

* item group, discount

* POS Redesign

* offline customer

* removed offline records from modal view
This commit is contained in:
rohitwaghchaure 2017-02-01 18:07:22 +05:30 committed by Rushabh Mehta
parent 929c3890a4
commit c13dbd408f
10 changed files with 658 additions and 392 deletions

View File

@ -30,6 +30,7 @@ def get_pos_data():
'doc': doc,
'default_customer': pos_profile.get('customer'),
'items': get_items_list(pos_profile),
'item_groups': get_item_group(pos_profile),
'customers': get_customers_list(pos_profile),
'serial_no_data': get_serial_no_data(pos_profile, doc.company),
'batch_no_data': get_batch_no_data(),
@ -140,6 +141,15 @@ def get_items_list(pos_profile):
disabled = 0 and has_variants = 0 and is_sales_item = 1 and {cond}
""".format(cond=cond), tuple(item_groups), as_dict=1)
def get_item_group(pos_profile):
if pos_profile.get('item_groups'):
item_groups = []
for d in pos_profile.get('item_groups'):
item_groups.extend(get_child_nodes('Item Group', d.item_group))
return item_groups
else:
return frappe.db.sql_list("""Select name from `tabItem Group` order by name""")
def get_customers_list(pos_profile):
cond = "1=1"
customer_groups = []
@ -276,11 +286,32 @@ def validate_customer(doc):
customer_doc = frappe.new_doc('Customer')
customer_doc.customer_name = doc.get('customer')
customer_doc.customer_type = 'Company'
customer_doc.customer_group = doc.get('customer_group')
customer_doc.territory = doc.get('territory')
customer_doc.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group')
customer_doc.territory = frappe.db.get_single_value('Selling Settings', 'territory')
customer_doc.flags.ignore_mandatory = True
customer_doc.save(ignore_permissions = True)
frappe.db.commit()
doc['customer'] = customer_doc.name
if doc.get('contact_details'):
args = json.loads(doc.get("contact_details"))
make_address(doc, args, customer_doc.name)
def make_address(doc, args, customer):
if args.get("address_line1"):
address = frappe.new_doc('Address')
address.address_line1 = args.get('address_line1')
address.address_line2 = args.get('address_line2')
address.city = args.get('city')
address.state = args.get('state')
address.zip_code = args.get('zip_code')
address.email_id = args.get('email_id')
address.flags.ignore_mandatory = True
address.country = frappe.db.get_value('Company', doc.get('company'), 'country')
address.append('links',{
'link_doctype': 'Customer',
'link_name': customer
})
address.save(ignore_permissions = True)
def validate_item(doc):
for item in doc.get('items'):

View File

@ -28,7 +28,7 @@
"label": "",
"length": 0,
"no_copy": 0,
"options": "fa fa-user",
"options": "icon-user",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -232,7 +232,7 @@
"in_standard_filter": 0,
"label": "Offline POS Name",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -1035,7 +1035,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart",
"options": "icon-shopping-cart",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -1122,7 +1122,7 @@
"label": "Packing List",
"length": 0,
"no_copy": 0,
"options": "fa fa-suitcase",
"options": "icon-suitcase",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -1462,7 +1462,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -1941,7 +1941,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -2238,7 +2238,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -2327,7 +2327,7 @@
"label": "Payments",
"length": 0,
"no_copy": 0,
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -3252,7 +3252,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
"options": "icon-file-text",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -3515,7 +3515,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-group",
"options": "icon-group",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -3720,7 +3720,7 @@
"label": "Recurring",
"length": 0,
"no_copy": 0,
"options": "fa fa-time",
"options": "icon-time",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -4173,7 +4173,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-file-text",
"icon": "icon-file-text",
"idx": 181,
"image_view": 0,
"in_create": 0,
@ -4282,5 +4282,6 @@
"sort_order": "DESC",
"timeline_field": "customer",
"title_field": "title",
"track_changes": 1,
"track_seen": 0
}

View File

@ -17,9 +17,9 @@ frappe.pages['pos'].refresh = function(wrapper) {
}
}
erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
init: function (wrapper) {
this.page_len = 20;
this.page = wrapper.page;
this.wrapper = $(wrapper).find('.page-content');
this.set_indicator();
@ -82,10 +82,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
me.create_new();
})
this.page.add_menu_item(__("View Offline Records"), function(){
me.show_unsync_invoice_list();
});
this.page.add_menu_item(__("Sync Master Data"), function () {
me.get_data_from_server(function () {
me.load_data(false);
@ -104,60 +100,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
},
show_unsync_invoice_list: function(){
var me = this;
this.si_docs = this.get_doc_from_localstorage();
this.list_dialog = new frappe.ui.Dialog({
title: 'Invoice List'
});
this.list_dialog.show();
this.list_body = this.list_dialog.body;
if(me.pos_profile_data["allow_delete"]) {
this.list_dialog.set_primary_action(__("Delete"), function() {
frappe.confirm(__("Delete permanently?"), function () {
me.delete_records();
})
}).addClass("btn-danger");
this.toggle_primary_action();
}
if(this.si_docs.length > 0){
me.render_offline_data();
me.dialog_actions()
}else{
$(this.list_body).append(repl('<div class="media-heading">%(message)s</div>', {'message': __("All records are synced.")}))
}
},
render_offline_data: function() {
var me = this;
this.removed_items = [];
$(this.list_body).empty();
$(this.list_body).append('<div class="row list-row list-row-head pos-invoice-list">\
<div class="col-xs-1"><input class="list-select-all" type="checkbox"></div>\
<div class="col-xs-3">Customer</div>\
<div class="col-xs-2 text-left">Status</div>\
<div class="col-xs-3 text-right">Paid Amount</div>\
<div class="col-xs-3 text-right">Grand Total</div>\
</div>')
$.each(this.si_docs, function(index, data){
for(key in data) {
$(frappe.render_template("pos_invoice_list", {
sr: index + 1,
name: key,
customer: data[key].customer,
paid_amount: format_currency(data[key].paid_amount, me.frm.doc.currency),
grand_total: format_currency(data[key].grand_total, me.frm.doc.currency),
data: me.get_doctype_status(data[key])
})).appendTo($(me.list_body));
}
})
},
dialog_actions: function () {
var me = this;
@ -201,7 +143,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.set_missing_values();
this.refresh(false);
this.disable_input_field();
this.list_dialog.hide();
this.list_dialog && this.list_dialog.hide();
}
},
@ -209,17 +151,18 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var me = this;
this.remove_doc_from_localstorage()
this.update_localstorage();
this.render_offline_data();
this.dialog_actions();
this.toggle_primary_action();
},
toggle_primary_action: function () {
var me = this;
if(this.frm.doc.allow_delete) {
if (this.removed_items && this.removed_items.length > 0) {
$(this.list_dialog.wrapper).find('.btn-danger').show();
$(this.wrapper).find('.btn-danger').show();
} else {
$(this.list_dialog.wrapper).find('.btn-danger').hide();
$(this.wrapper).find('.btn-danger').hide();
}
}
},
@ -287,6 +230,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var me = this;
this.meta = r.message.meta;
this.item_data = r.message.items;
this.item_groups = r.message.item_groups;
this.customers = r.message.customers;
this.serial_no_data = r.message.serial_no_data;
this.batch_no_data = r.message.batch_no_data;
@ -303,14 +247,14 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
save_previous_entry: function () {
if (this.frm.doc.docstatus < 1 && this.frm.doc.items.length > 0) {
this.create_invoice()
this.create_invoice();
}
},
create_new: function () {
var me = this;
this.frm = {}
this.name = '';
this.name = null;
this.load_data(true);
this.setup();
},
@ -330,12 +274,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
locals["DocType"][data.name] = data;
})
this.print_template_data = frappe.render_template("print_template", {content: this.print_template,
this.print_template_data = frappe.render_template("print_template", {
content: this.print_template,
title: "POS", base_url: frappe.urllib.get_base_url(), print_css: frappe.boot.print_css,
print_settings: this.print_settings, header: this.letter_head.header, footer: this.letter_head.footer})
print_settings: this.print_settings, header: this.letter_head.header, footer: this.letter_head.footer
})
},
setup: function () {
this.frm.doc.allow_delete = this.pos_profile_data["allow_delete"];
this.wrapper.html(frappe.render_template("pos", this.frm.doc));
this.set_transaction_defaults("Customer");
this.make();
@ -353,6 +300,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
make: function () {
this.make_search();
this.make_list_customers();
this.make_customer();
this.make_item_list();
this.make_discount_field()
@ -360,25 +308,44 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
make_search: function () {
var me = this;
this.search = frappe.ui.form.make_control({
this.serach_item = frappe.ui.form.make_control({
df: {
"fieldtype": "Data",
"label": "Item",
"fieldname": "pos_item",
"placeholder": __("Search Item")
},
parent: this.wrapper.find(".search-area"),
parent: this.wrapper.find(".search-item"),
only_input: true,
});
this.search.make_input();
this.search.$input.on("keyup", function() {
this.serach_item.make_input();
this.serach_item.$input.on("keyup", function () {
setTimeout(function () {
me.items = me.get_items();
me.make_item_list();
}, 1000);
});
this.search_item_group = frappe.ui.form.make_control({
df: {
"fieldtype": "Select",
"options": me.item_groups,
"label": __("Item Group"),
"fieldname": "item_group",
"placeholder": __("Item Group")
},
parent: this.wrapper.find(".search-item-group"),
only_input: true,
});
this.search_item_group.make_input();
this.search_item_group.$input.on("change", function () {
me.page_len = 20;
me.items = me.get_items();
me.make_item_list();
});
this.party_field = frappe.ui.form.make_control({
df: {
"fieldtype": "Data",
@ -392,12 +359,129 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
this.party_field.make_input();
this.set_focus()
setTimeout(this.set_focus.bind(this), 500);
this.wrapper.find(".btn-more").on("click", function() {
me.page_len += 20;
me.make_item_list();
})
},
make_list_customers: function () {
var me = this;
this.list_customers_btn = this.wrapper.find('.list-customers-btn');
this.add_customer_btn = this.wrapper.find('.add-customer-btn');
this.pos_bill = this.wrapper.find('.pos-bill').hide();
this.list_customers = this.wrapper.find('.list-customers');
this.list_customers_btn.on('click', function () {
$(this).toggleClass("view_customer");
if($(this).hasClass("view_customer")) {
me.render_list_customers();
me.bind_delete_event()
me.party_field.$input.attr('disabled', true);
me.list_customers.show();
me.pos_bill.hide();
} else {
if(me.frm.doc.docstatus == 0) {
me.party_field.$input.attr('disabled', false);
}
me.pos_bill.show();
me.list_customers.hide()
}
});
this.add_customer_btn.on('click', function() {
me.save_previous_entry();
me.create_new();
me.refresh();
me.set_focus();
});
},
render_list_customers: function () {
var me = this;
this.removed_items = [];
this.list_customers.empty();
this.si_docs = this.get_doc_from_localstorage();
if (!this.si_docs.length) {
this.list_customers.append(
'<div style="padding: 12px; margin-left:-12px;">' + __("No offline records.") + '</div>'
)
return;
}
var html = '<div class="row list-row list-row-head pos-invoice-list">\
<div class="col-xs-1"><input class="list-select-all" type="checkbox"></div>\
<div class="col-xs-3">Customer</div>\
<div class="col-xs-2 text-left">Status</div>\
<div class="col-xs-3 text-right">Paid Amount</div>\
<div class="col-xs-3 text-right">Grand Total</div>\
</div>';
this.si_docs.forEach(function (data, i) {
for (key in data) {
html += frappe.render_template("pos_invoice_list", {
sr: i + 1,
name: key,
customer: data[key].customer,
paid_amount: format_currency(data[key].paid_amount, me.frm.doc.currency),
grand_total: format_currency(data[key].grand_total, me.frm.doc.currency),
data: me.get_doctype_status(data[key])
});
}
});
this.list_customers.append(html);
this.list_customers.find('.list-column').click(function () {
me.list_customers.hide();
me.list_customers_btn.toggleClass("view_customer");
me.pos_bill.show();
me.list_customers_btn.show();
me.name = $(this).parents().attr('invoice-name')
me.edit_record();
})
//actions
$(this.wrapper).find('.list-select-all').click(function () {
me.list_customers.find('.list-delete').prop("checked", $(this).is(":checked"))
me.removed_items = [];
if ($(this).is(":checked")) {
$.each(me.si_docs, function (index, data) {
for (key in data) {
me.removed_items.push(key)
}
});
}
me.toggle_primary_action();
});
$(this.wrapper).find('.list-delete').click(function () {
me.name = $(this).parent().parent().attr('invoice-name');
if ($(this).is(":checked")) {
me.removed_items.push(me.name);
} else {
me.removed_items.pop(me.name)
}
me.toggle_primary_action();
});
},
bind_delete_event: function() {
var me = this;
$(this.wrapper).find('.btn-danger').click(function(){
frappe.confirm(__("Delete permanently?"), function () {
me.delete_records();
me.render_list_customers();
})
})
},
set_focus: function () {
if(this.default_customer){
this.search.$input.focus();
if (this.default_customer || this.frm.doc.customer) {
this.serach_item.$input.focus();
} else {
this.party_field.$input.focus();
}
@ -433,7 +517,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
.get(0);
}
});
var items = this.customers.map(function(c) {
var customers = this.customers.map(function (c) {
return {
label: c.name,
value: c.name,
@ -441,7 +525,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
territory: c.territory
}
});
items.push({
customers.push({
label: "<span class='text-primary link-option'>"
+ "<i class='fa fa-plus' style='margin-right: 5px;'></i> "
+ __("Create a new Customer")
@ -449,22 +534,24 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
value: 'is_action',
action: me.new_customer
});
this.party_field.awesomeplete.list = items;
this.party_field.awesomeplete.list = customers;
this.party_field.$input
.on('input', function (e) {
me.party_field.awesomeplete.list = items;
me.party_field.awesomeplete.list = customers;
})
.on('awesomplete-select', function (e) {
var item = me.party_field.awesomeplete
var customer = me.party_field.awesomeplete
.get_item(e.originalEvent.text.value);
if(!item) return;
if(item.action) {
item.action.apply(me);
if (!customer) return;
// create customer link
if (customer.action) {
customer.action.apply(me);
return;
}
me.update_customer_data(item);
me.update_customer_data(customer);
me.refresh();
me.set_focus();
})
.on('change', function (e) {
if (!e.originalEvent.text) {
@ -487,11 +574,84 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
new_customer: function () {
var me = this;
if (!this.connection_status) return;
frappe.ui.form.quick_entry('Customer', function (doc) {
me.customers.push(doc);
me.party_field.$input.val(doc.name);
me.update_customer_data(doc);
this.customer_doc = new frappe.ui.Dialog({
'title': 'Customer',
fields: [
{
"label": __("Full Name"),
"fieldname": "full_name",
"fieldtype": "Data",
"reqd": 1
},
{
"fieldtype": "Section Break"
},
{
"label": __("Email Id"),
"fieldname": "email_id",
"fieldtype": "Data"
},
{
"fieldtype": "Column Break"
},
{
"label": __("Contact Number"),
"fieldname": "contact_no",
"fieldtype": "Data"
},
{
"fieldtype": "Section Break"
},
{
"label": __("Address Line 1"),
"fieldname": "address_line1",
"fieldtype": "Data"
},
{
"label": __("Address Line 2"),
"fieldname": "address_line2",
"fieldtype": "Data"
},
{
"fieldtype": "Column Break"
},
{
"label": __("City"),
"fieldname": "city",
"fieldtype": "Data"
},
{
"label": __("State"),
"fieldname": "state",
"fieldtype": "Data"
},
{
"label": __("ZIP Code"),
"fieldname": "zip_code",
"fieldtype": "Data"
}
]
})
this.customer_doc.show()
this.customer_doc.set_primary_action(__("Save"), function () {
me.make_offline_customer();
me.pos_bill.show();
});
},
make_offline_customer: function() {
this.frm.doc.customer = this.customer_doc.get_values().full_name;
this.frm.doc.contact_details = JSON.stringify(this.customer_doc.get_values());
this.party_field.$input.val(this.frm.doc.customer);
this.customers.push({
name: this.frm.doc.customer,
customer_name: this.frm.doc.customer
});
this.customer_doc.hide()
},
update_customer_data: function (doc) {
@ -500,6 +660,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.frm.doc.customer_name = doc.customer_name;
this.frm.doc.customer_group = doc.customer_group;
this.frm.doc.territory = doc.territory;
this.pos_bill.show();
},
get_customers: function (key) {
@ -536,7 +697,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if (this.items.length > 0) {
$.each(this.items, function(index, obj) {
if(index < 30){
if(index < me.page_len){
$(frappe.render_template("pos_item", {
item_code: obj.name,
item_price: format_currency(me.price_list_data[obj.name], me.frm.doc.currency),
@ -552,13 +713,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}
if (this.items.length == 1
&& this.search.$input.val()) {
this.search.$input.val("");
&& this.serach_item.$input.val()) {
this.serach_item.$input.val("");
this.add_to_cart();
}
// if form is local then allow this function
$(me.wrapper).find("div.pos-item").on("click", function () {
if(me.list_customers_btn.hasClass("view_customer")) return;
me.customer_validate();
if (me.frm.doc.docstatus == 0) {
me.items = me.get_items($(this).attr("data-item-code"))
@ -582,25 +745,27 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
})
}
key = this.search.$input.val().toLowerCase().replace(/[&\/\\#,+()\[\]$~.'":*?<>{}]/g,'\\$&');
this.items_list = this.apply_category();
key = this.serach_item.$input.val().toLowerCase().replace(/[&\/\\#,+()\[\]$~.'":*?<>{}]/g, '\\$&');
var re = new RegExp('%', 'g');
var reg = new RegExp(key.replace(re, '[\\w*\\s*[a-zA-Z0-9]*]*'))
search_status = true
if (key) {
return $.grep(this.item_data, function(item){
return $.grep(this.items_list, function (item) {
if (search_status) {
if(in_list(me.batch_no_data[item.item_code], me.search.$input.val())){
if (in_list(me.batch_no_data[item.item_code], me.serach_item.$input.val())) {
search_status = false;
return me.item_batch_no[item.item_code] = me.search.$input.val()
return me.item_batch_no[item.item_code] = me.serach_item.$input.val()
} else if (me.serial_no_data[item.item_code]
&& in_list(Object.keys(me.serial_no_data[item.item_code]), me.search.$input.val())) {
&& in_list(Object.keys(me.serial_no_data[item.item_code]), me.serach_item.$input.val())) {
search_status = false;
me.item_serial_no[item.item_code] = [me.search.$input.val(), me.serial_no_data[item.item_code][me.search.$input.val()]]
me.item_serial_no[item.item_code] = [me.serach_item.$input.val(), me.serial_no_data[item.item_code][me.serach_item.$input.val()]]
return true
} else if(item.barcode == me.search.$input.val()) {
} else if (item.barcode == me.serach_item.$input.val()) {
search_status = false;
return item.barcode == me.search.$input.val();
return item.barcode == me.serach_item.$input.val();
} else if (reg.test(item.item_code.toLowerCase()) || reg.test(item.description.toLowerCase()) ||
reg.test(item.item_name.toLowerCase()) || reg.test(item.item_group.toLowerCase())) {
return true
@ -608,7 +773,20 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}
})
} else {
return this.item_data;
return this.items_list;
}
},
apply_category: function() {
var me = this;
category = this.search_item_group.$input.val();
if(category == 'All Item Groups') {
return this.item_data
} else {
return this.item_data.filter(function(element, index, array){
return element.item_group == category;
});
}
},
@ -632,13 +810,25 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1;
me.update_qty(item_code, qty)
})
$(this.wrapper).find(".pos-item-discount").on("change", function () {
var item_code = $(this).parents(".pos-bill-item").attr("data-item-code");
var discount = $(this).val();
me.update_discount(item_code, discount)
})
},
update_qty: function (item_code, qty) {
var me = this;
this.items = this.get_items(item_code);
this.validate_serial_no()
this.update_qty_rate_against_item_code(item_code, "qty", qty);
this.set_item_details(item_code, "qty", qty);
},
update_discount: function(item_code, discount) {
var me = this;
this.items = this.get_items(item_code);
this.set_item_details(item_code, "discount_percentage", discount);
},
update_rate: function () {
@ -646,11 +836,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
$(this.wrapper).find(".pos-item-rate").on("change", function () {
var item_code = $(this).parents(".pos-bill-item").attr("data-item-code");
me.update_qty_rate_against_item_code(item_code, "rate", $(this).val());
me.set_item_details(item_code, "rate", $(this).val());
})
},
update_qty_rate_against_item_code: function(item_code, field, value){
set_item_details: function (item_code, field, value) {
var me = this;
if (value < 0) {
frappe.throw(__("Enter value must be positive"));
@ -658,11 +848,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.remove_item = []
$.each(this.frm.doc["items"] || [], function (i, d) {
if (d.item_code == item_code) {
if (d.serial_no && field == 'qty') {
me.validate_serial_no_qty(d, item_code, field, value)
}
if (d.item_code == item_code) {
d[field] = flt(value);
d.amount = flt(d.rate) * flt(d.qty);
if (d.qty == 0) {
@ -826,7 +1016,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
item_code: d.item_code,
item_name: (d.item_name === d.item_code || !d.item_name) ? "" : ("<br>" + d.item_name),
qty: d.qty,
actual_qty: me.actual_qty_dict[d.item_code] || 0,
discount_percentage: d.discount_percentage || 0.0,
actual_qty: me.actual_qty_dict[d.item_code] || 0.0,
projected_qty: d.projected_qty,
rate: format_number(d.rate, me.frm.doc.currency),
enabled: me.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
@ -838,6 +1029,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
$(this).select();
});
this.wrapper.find("input.pos-item-discount").on("focus", function () {
$(this).select();
});
this.wrapper.find("input.pos-item-rate").on("focus", function () {
$(this).select();
});
@ -878,7 +1073,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
me.update_paid_amount_status(true);
me.create_invoice();
me.make_payment();
}, "octicon octfa fa-credit-card");
}, "fa fa-credit-card");
} else if (this.frm.doc.docstatus == 1) {
this.page.set_primary_action(__("Print"), function () {
html = frappe.render(me.print_template_data, me.frm.doc)
@ -888,10 +1083,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.page.clear_primary_action()
}
this.page.set_secondary_action(__("New"), function() {
me.save_previous_entry();
me.create_new();
}, "octicon octfa fa-plus").addClass("btn-primary");
// this.page.set_secondary_action(__("New"), function () {
// me.save_previous_entry();
// me.create_new();
// }, "fa fa-plus").addClass("btn-primary");
},
print_dialog: function () {
@ -943,13 +1138,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
disable_input_field: function () {
var pointer_events = 'inherit'
$(this.wrapper).find('input').attr("disabled", false);
$(this.wrapper).find('select').attr("disabled", false);
if (this.frm.doc.docstatus == 1) {
pointer_events = 'none';
$(this.wrapper).find('input').attr("disabled", true);
$(this.wrapper).find('select').attr("disabled", true);
}
$(this.wrapper).find('.pos-bill-wrapper').css('pointer-events', pointer_events);
$(this.wrapper).find('.pos-bill').css('pointer-events', pointer_events);
$(this.wrapper).find('.pos-items-section').css('pointer-events', pointer_events);
this.set_primary_action();
},
@ -970,6 +1167,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.update_localstorage();
this.set_primary_action();
}
return invoice_data;
},
update_invoice: function () {
@ -1145,16 +1343,18 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
pricing_rule = me.get_pricing_rule(item)
me.validate_pricing_rule(pricing_rule)
if (pricing_rule.length) {
item.pricing_rule = pricing_rule[0].name;
item.margin_type = pricing_rule[0].margin_type;
item.price_list_rate = pricing_rule[0].price || item.price_list_rate;
item.margin_rate_or_amount = pricing_rule[0].margin_rate_or_amount;
item.discount_percentage = pricing_rule[0].discount_percentage || 0.0;
me.apply_pricing_rule_on_item(item)
} else if(item.discount_percentage > 0 || item.margin_rate_or_amount > 0) {
} else if ((item.discount_percentage > 0 || item.margin_rate_or_amount > 0) && item.pricing_rule) {
item.margin_rate_or_amount = 0.0;
item.discount_percentage = 0.0;
me.apply_pricing_rule_on_item(item)
item.pricing_rule = null;
}
me.apply_pricing_rule_on_item(item)
})
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

@ -67,7 +67,6 @@
.pos-toolbar,
.pos-bill-toolbar {
padding: 10px 0px;
border-bottom: 1px solid #d1d8dd;
height: 51px;
}
.pos-item-toolbar .form-group {
@ -79,6 +78,7 @@
margin-right: -1px;
}
.pos-bill {
border-top: 1px solid #d1d8dd;
margin-left: -15px;
margin-right: -15px;
}
@ -171,6 +171,9 @@
.payment-toolbar {
padding-right: 30px;
}
.list-row-head.pos-invoice-list {
border-top: 1px solid #d1d8dd;
}
body[data-route="pos"] .modal-dialog {
width: 750px;
}

View File

@ -1,14 +1,26 @@
<div class="pos">
<div class="row">
<div class="col-sm-5 pos-bill-wrapper">
<div class="pos-bill-toolbar row">
<div class="party-area col-xs-12"></div>
<div class="col-sm-6 pos-bill-wrapper">
<div class="pos-bill-toolbar" style="display: flex;">
<div class="party-area" style="flex: 1;"></div>
<button class="btn btn-default list-customers-btn" style="margin: 0 5px 0 15px;">
<i class="fa fa-list"></i>
</button>
<button class="btn btn-default add-customer-btn">
<i class="fa fa-plus"></i>
</button>
{% if (allow_delete) { %}
<button class="btn btn-default btn-danger" style="margin: 0 5px 0 5px;display:none">
<i class="octicon octicon-trashcan"></i>
</button>
{% } %}
</div>
<div class="pos-bill">
<div class="item-cart">
<div class="row pos-bill-row pos-bill-header">
<div class="col-xs-5"><h6>{%= __("Item") %}</h6></div>
<div class="col-xs-4"><h6 class="text-right">{%= __("Quantity") %}</h6></div>
<div class="col-xs-4"><h6>{%= __("Item") %}</h6></div>
<div class="col-xs-3"><h6 class="text-right">{%= __("Quantity") %}</h6></div>
<div class="col-xs-2"><h6 class="text-right">{%= __("Discount") %}</h6></div>
<div class="col-xs-3"><h6 class="text-right">{%= __("Rate") %}</h6></div>
</div>
<div class="items"></div>
@ -48,18 +60,27 @@
</div>
</div>
</div>
<div class="list-customers">
</div>
<div class="col-sm-7 pos-items-section">
</div>
<div class="col-sm-6 pos-items-section">
<div class="row pos-item-area">
</div>
<span id="customer-results" style="color:#68a;"></span>
<div class="row pos-item-toolbar">
<div class="search-area col-xs-12"></div>
<div class="search-item-group col-xs-5"></div>
<div class="search-item col-xs-7"></div>
</div>
<div class="item-list-area">
<div class="item-list-area" style="auto">
<div class="app-listing item-list"></ul>
</div>
</div>
<div class="row">
<div class="text-right list-paging-area">
<button class="btn btn-default btn-more btn-sm" style="margin:5px 20px">{{ __("More") }}</button>
</div>
</div>
</div>
</div>

View File

@ -1,25 +1,30 @@
<div class="row pos-bill-row pos-bill-item" data-item-code="{%= item_code %}">
<div class="col-xs-5"><h6>{%= item_code || "" %}{%= item_name || "" %}</h6></div>
<div class="col-xs-4">
<div class="col-xs-4"><h6>{%= item_code || "" %}{%= __(item_name) || "" %}</h6></div>
<div class="col-xs-3">
<div class="row pos-qty-row">
<div class="col-xs-2 text-center pos-qty-btn" data-action="decrease-qty"><i class="fa fa-minus text-muted" style="font-size:12px"></i></div>
<div class="col-xs-8">
<div>
<input type="text" value="{%= qty %}" class="form-control input-sm pos-item-qty text-right">
<input type="tel" value="{%= qty %}" class="form-control pos-item-qty text-right">
</div>
{% if(actual_qty != null) { %}
<div style="margin-top: 5px;" class="text-muted small text-right">
<span title="{%= __("In Stock") %}">{%= actual_qty || 0 %}<span>
{%= __("In Stock: ") %} <span>{%= actual_qty || 0.0 %}</span>
</div>
{% } %}
</div>
<div class="col-xs-2 text-center pos-qty-btn" data-action="increase-qty"><i class="fa fa-plus text-muted" style="font-size:12px"></i></div>
</div>
</div>
<div class="col-xs-2 text-right">
<div class="row input-sm">
<input type="tel" value="{%= discount_percentage %}" class="form-control text-right pos-item-discount">
</div>
</div>
<div class="col-xs-3 text-right">
<div class="text-muted" style="margin-top: 5px;">
{% if(enabled) { %}
<input type="text" value="{%= rate %}" class="form-control input-sm pos-item-rate text-right">
<input type="tel" value="{%= rate %}" class="form-control input-sm pos-item-rate text-right">
{% } else { %}
<h6>{%= format_currency(rate) %}</h6>
{% } %}

View File

@ -82,7 +82,7 @@
.pos-toolbar, .pos-bill-toolbar {
padding: 10px 0px;
border-bottom: 1px solid #d1d8dd;
// border-bottom: 1px solid #d1d8dd;
height: 51px;
}
@ -97,6 +97,7 @@
}
.pos-bill {
border-top: 1px solid @border-color;
margin-left: -15px;
margin-right: -15px;
}
@ -213,6 +214,10 @@
padding-right: 30px;
}
.list-row-head.pos-invoice-list {
border-top: 1px solid @border-color;
}
body[data-route="pos"] .modal-dialog {
width: 750px;