Update cart ui from cur_frm, Add number pad
diff --git a/erpnext/public/css/pos.css b/erpnext/public/css/pos.css
index 399613d..de1e097 100644
--- a/erpnext/public/css/pos.css
+++ b/erpnext/public/css/pos.css
@@ -23,6 +23,9 @@
   width: 40%;
   margin-left: 15px;
 }
+.cart-wrapper {
+  margin-bottom: 10px;
+}
 .cart-wrapper .list-item__content:not(:first-child) {
   justify-content: flex-end;
 }
@@ -30,6 +33,10 @@
   height: 200px;
   overflow: auto;
 }
+.cart-items input {
+  height: 22px;
+  font-size: 12px;
+}
 .fields {
   display: flex;
 }
@@ -63,3 +70,38 @@
   left: 50%;
   transform: translate(-50%, -50%);
 }
+@keyframes yellow-fade {
+  0% {
+    background-color: #fffce7;
+  }
+  100% {
+    background-color: transparent;
+  }
+}
+.highlight {
+  animation: yellow-fade 1s ease-in 1;
+}
+input[type=number]::-webkit-inner-spin-button,
+input[type=number]::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
+.number-pad {
+  border-collapse: collapse;
+  cursor: pointer;
+  display: table;
+  margin: auto;
+}
+.num-row {
+  display: table-row;
+}
+.num-col {
+  display: table-cell;
+  border: 1px solid #d1d8dd;
+}
+.num-col > div {
+  width: 50px;
+  height: 50px;
+  text-align: center;
+  line-height: 50px;
+}
diff --git a/erpnext/public/less/pos.less b/erpnext/public/less/pos.less
index 9358f0a..de16514 100644
--- a/erpnext/public/less/pos.less
+++ b/erpnext/public/less/pos.less
@@ -35,6 +35,7 @@
 }
 
 .cart-wrapper {
+	margin-bottom: 10px;
 	.list-item__content:not(:first-child) {
 		justify-content: flex-end;
 	}
@@ -43,6 +44,11 @@
 .cart-items {
 	height: 200px;
 	overflow: auto;
+
+	input {
+		height: 22px;
+		font-size: @text-medium;
+	}
 }
 
 .fields {
@@ -84,4 +90,42 @@
 		left: 50%;
 		transform: translate(-50%, -50%);
 	}
+}
+
+@keyframes yellow-fade {
+	0% {background-color: @light-yellow;}
+	100% {background-color: transparent;}
+}
+
+.highlight {
+	animation: yellow-fade 1s ease-in 1;
+}
+
+input[type=number]::-webkit-inner-spin-button,
+input[type=number]::-webkit-outer-spin-button {
+	-webkit-appearance: none;
+	margin: 0;
+}
+
+// number pad
+
+.number-pad {
+	border-collapse: collapse;
+	cursor: pointer;
+	display: table;
+	margin: auto;
+}
+.num-row {
+	display: table-row;
+}
+.num-col {
+	display: table-cell;
+	border: 1px solid @border-color;
+
+	& > div {
+		width: 50px;
+		height: 50px;
+		text-align: center;
+		line-height: 50px;
+	}
 }
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index c28a5bd..19eb70e 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -1,3 +1,5 @@
+/* global Clusterize */
+
 frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
 	var page = frappe.ui.make_app_page({
 		parent: wrapper,
@@ -5,11 +7,11 @@
 		single_column: true
 	});
 
-	wrapper.pos = new erpnext.PointOfSale(wrapper);
-	cur_pos = wrapper.pos;
+	wrapper.pos = new PointOfSale(wrapper);
+	window.cur_pos = wrapper.pos;
 }
 
-erpnext.PointOfSale = class PointOfSale {
+class PointOfSale {
 	constructor(wrapper) {
 		this.wrapper = $(wrapper).find('.layout-main-section');
 		this.page = wrapper.page;
@@ -20,24 +22,25 @@
 		];
 
 		frappe.require(assets, () => {
-			this.prepare().then(() => {
-				this.make();
-				this.bind_events();
-			});
+			this.make();
 		});
 	}
 
-	prepare() {
-		this.set_online_status();
-		this.prepare_menu();
-		this.make_sales_invoice_frm()
-		return this.get_pos_profile();
-	}
-
 	make() {
-		this.make_dom();
-		this.make_cart();
-		this.make_items();
+		return frappe.run_serially([
+			() => {
+				this.prepare_dom();
+				this.prepare_menu();
+				this.set_online_status();
+			},
+			() => this.make_sales_invoice_frm(),
+			() => this.setup_pos_profile(),
+			() => {
+				this.make_cart();
+				this.make_items();
+				this.bind_events();
+			}
+		]);
 	}
 
 	set_online_status() {
@@ -54,7 +57,7 @@
 		});
 	}
 
-	make_dom() {
+	prepare_dom() {
 		this.wrapper.append(`
 			<div class="pos">
 				<section class="cart-container">
@@ -68,29 +71,64 @@
 	}
 
 	make_cart() {
-		this.cart = new erpnext.POSCart(this.wrapper.find('.cart-container'));
-	}
-
-	make_items() {
-		this.items = new erpnext.POSItems({
-			wrapper: this.wrapper.find('.item-container'),
-			pos_profile: this.pos_profile,
+		this.cart = new POSCart({
+			wrapper: this.wrapper.find('.cart-container'),
 			events: {
-				item_click: (item_code) => this.add_item_to_cart(item_code)
+				customer_change: (customer) => this.cur_frm.set_value('customer', customer),
+				increase_qty: (item_code) => {
+					this.add_item_to_cart(item_code);
+				},
+				decrease_qty: (item_code) => {
+					this.add_item_to_cart(item_code, -1);
+				}
 			}
 		});
 	}
 
-	add_item_to_cart(item_code) {
-		const item = this.items.get(item_code);
-		this.cart.add_item(item);
+	make_items() {
+		this.items = new POSItems({
+			wrapper: this.wrapper.find('.item-container'),
+			pos_profile: this.pos_profile,
+			events: {
+				item_click: (item_code) => {
+					if(!this.cur_frm.doc.customer) {
+						frappe.throw(__('Please select a customer'));
+					}
+					this.add_item_to_cart(item_code);
+				}
+			}
+		});
+	}
+
+	add_item_to_cart(item_code, qty = 1) {
+
+		if(this.cart.exists(item_code)) {
+			// increase qty by 1
+			this.cur_frm.doc.items.forEach((item) => {
+				if (item.item_code === item_code) {
+					frappe.model.set_value(item.doctype, item.name, 'qty', item.qty + qty);
+					// update cart
+					this.cart.add_item(item);
+				}
+			});
+			return;
+		}
+
+		// add to cur_frm
+		const item = this.cur_frm.add_child('items', { item_code: item_code });
+		this.cur_frm.script_manager
+			.trigger('item_code', item.doctype, item.name)
+			.then(() => {
+				// update cart
+				this.cart.add_item(item);
+			});
 	}
 
 	bind_events() {
 
 	}
 
-	get_pos_profile() {
+	setup_pos_profile() {
 		return frappe.call({
 			method: 'erpnext.stock.get_item_details.get_pos_profile',
 			args: {
@@ -104,13 +142,14 @@
 	make_sales_invoice_frm() {
 		const dt = 'Sales Invoice';
 		return new Promise(resolve => {
-			frappe.model.with_doctype(dt, function() {
+			frappe.model.with_doctype(dt, () => {
 				const page = $('<div>');
 				const frm = new _f.Frm(dt, page, false);
 				const name = frappe.model.make_new_doc_and_get_name(dt, true);
 				frm.refresh(name);
 				frm.doc.items = [];
-				resolve(frm);
+				this.cur_frm = frm;
+				resolve();
 			});
 		});
 	}
@@ -141,16 +180,18 @@
 	}
 }
 
-erpnext.POSCart = class POSCart {
-	constructor(wrapper) {
+class POSCart {
+	constructor({wrapper, events}) {
 		this.wrapper = wrapper;
-		this.items = {};
+		this.events = events;
 		this.make();
+		this.bind_events();
 	}
 
 	make() {
 		this.make_dom();
 		this.make_customer_field();
+		this.make_numpad();
 	}
 
 	make_dom() {
@@ -172,7 +213,10 @@
 					</div>
 				</div>
 			</div>
+			<div class="number-pad-container">
+			</div>
 		`);
+		this.$cart_items = this.wrapper.find('.cart-items');
 	}
 
 	make_customer_field() {
@@ -181,8 +225,9 @@
 				fieldtype: 'Link',
 				label: 'Customer',
 				options: 'Customer',
+				reqd: 1,
 				onchange: (e) => {
-					cur_frm.set_value('customer', this.customer_field.value);
+					this.events.customer_change.apply(null, [this.customer_field.get_value()]);
 				}
 			},
 			parent: this.wrapper.find('.customer-field'),
@@ -190,96 +235,113 @@
 		});
 	}
 
-	add_item(item) {
-		const { item_code } = item;
-		const _item = this.items[item_code];
-
-		if (_item) {
-			// exists, increase quantity
-			_item.quantity += 1;
-			this.update_quantity(_item);
-		} else {
-			// add it to this.items
-			item['qty'] = 1;
-			this.child = cur_frm.add_child('items', item)
-			cur_frm.script_manager.trigger("item_code", this.child.doctype, this.child.name);
-
-			const _item = {
-				doc: item,
-				quantity: 1,
-				discount: 2,
-				rate: 2
+	make_numpad() {
+		this.numpad = new NumberPad({
+			wrapper: this.wrapper.find('.number-pad-container'),
+			onclick: (btn_value) => {
+				// on click
+				console.log(btn_value);
 			}
-			Object.assign(this.items, {
-				[item_code]: _item
-			});
-			this.add_item_to_cart(_item);
-		}
+		});
 	}
 
-	add_item_to_cart(item) {
+	add_item(item) {
 		this.wrapper.find('.cart-items .empty-state').hide();
-		const $item = $(this.get_item_html(item))
-		$item.appendTo(this.wrapper.find('.cart-items'));
-		// $item.addClass('added');
-		// this.wrapper.find('.cart-items').append(this.get_item_html(item))
-	}
 
-	update_quantity(item) {
-		this.wrapper.find(`.list-item[data-item-name="${item.doc.item_code}"] .quantity`)
-			.text(item.quantity);
-
-			$.each(cur_frm.doc["items"] || [], function(i, d) {
-				if (d.item_code == item.doc.item_code) {
-					frappe.model.set_value(d.doctype, d.name, "qty", d.qty + 1);
-				}
-			});
-	}
-
-	remove_item(item_code) {
-		delete this.items[item_code];
-
-		// this.refresh();
-	}
-
-	refresh() {
-		const item_codes = Object.keys(this.items);
-		const html = item_codes
-			.map(item_code => this.get_item_html(item_code))
-			.join("");
-		this.wrapper.find('.cart-items').html(html);
-	}
-
-	get_item_html(_item) {
-
-		let item;
-		if (typeof _item === "object") {
-			item = _item;
+		if (this.exists(item.item_code)) {
+			// update quantity
+			this.update_item(item);
+		} else {
+			// add to cart
+			const $item = $(this.get_item_html(item));
+			$item.appendTo(this.$cart_items);
 		}
-		else if (typeof _item === "string") {
-			item = this.items[_item];
-		}
+		this.highlight_item(item.item_code);
+		this.scroll_to_item(item.item_code);
+	}
 
+	update_item(item) {
+		const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`);
+		if(item.qty > 0) {
+			$item.find('.quantity input').val(item.qty);
+			$item.find('.discount').text(item.discount_percentage);
+			$item.find('.rate').text(item.rate);
+		} else {
+			$item.remove();
+		}
+	}
+
+	exists(item_code) {
+		let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
+		return $item.length > 0;
+	}
+
+	highlight_item(item_code) {
+		const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
+		$item.addClass('highlight');
+		setTimeout(() => $item.removeClass('highlight'), 1000);
+	}
+
+	scroll_to_item(item_code) {
+		const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
+		const scrollTop = $item.offset().top - this.$cart_items.offset().top + this.$cart_items.scrollTop();
+		this.$cart_items.animate({ scrollTop });
+	}
+
+	get_item_html(item) {
 		return `
-			<div class="list-item" data-item-name="${item.doc.item_code}">
+			<div class="list-item" data-item-code="${item.item_code}">
 				<div class="item-name list-item__content list-item__content--flex-2 ellipsis">
-					${item.doc.item_name}
+					${item.item_name}
 				</div>
 				<div class="quantity list-item__content text-right">
-					${item.quantity}
+					${get_quantity_html(item.qty)}
 				</div>
 				<div class="discount list-item__content text-right">
-					${item.discount}
+					${item.discount_percentage}%
 				</div>
 				<div class="rate list-item__content text-right">
 					${item.rate}
 				</div>
 			</div>
 		`;
+
+		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>
+			`;
+		}
+	}
+
+	bind_events() {
+		const events = this.events;
+		this.$cart_items.on('click',
+			'[data-action="increment"], [data-action="decrement"]', function() {
+				const $btn = $(this);
+				const $item = $btn.closest('.list-item[data-item-code]');
+				const item_code = $item.attr('data-item-code');
+				const action = $btn.attr('data-action');
+
+				if(action === 'increment') {
+					events.increase_qty(item_code);
+				} else if(action === 'decrement') {
+					events.decrease_qty(item_code);
+				}
+			});
 	}
 }
 
-erpnext.POSItems = class POSItems {
+class POSItems {
 	constructor({wrapper, pos_profile, events}) {
 		this.wrapper = wrapper;
 		this.pos_profile = pos_profile;
@@ -439,16 +501,10 @@
 						<div class="image-field"
 							style="${!item_image ? 'background-color: #fafbfc;' : ''} border: 0px;"
 						>
-							${!item_image ?
-								`<span class="placeholder-text">
+							${!item_image ? `<span class="placeholder-text">
 									${frappe.get_abbr(item_title)}
-								</span>` :
-								''
-							}
-							${item_image ?
-								`<img src="${item_image}" alt="${item_title}">` :
-								''
-							}
+								</span>` : '' }
+							${item_image ? `<img src="${item_image}" alt="${item_title}">` : '' }
 						</div>
 						<span class="price-info">
 							${item_price}
@@ -509,12 +565,12 @@
 						"`tabItem`.`end_of_life`",
 						"`tabItem`.`total_projected_qty`"
 					],
+					filters: [['disabled', '=', '0']],
 					order_by: "`tabItem`.`modified` desc",
 					page_length: page_length,
 					start: start
 				}
-			})
-			.then(r => {
+			}).then(r => {
 				const data = r.message;
 				const items = frappe.utils.dict(data.keys, data.values);
 
@@ -528,4 +584,52 @@
 			});
 		});
 	}
+}
+
+class NumberPad {
+	constructor({wrapper, onclick}) {
+		this.wrapper = wrapper;
+		this.onclick = onclick;
+		this.make_dom();
+		this.bind_events();
+	}
+
+	make_dom() {
+		const button_array = [
+			[1, 2, 3, 'Qty'],
+			[4, 5, 6, 'Disc'],
+			[7, 8, 9, 'Price'],
+			['Del', 0, '.', 'Pay']
+		];
+
+		this.wrapper.html(`
+			<div class="number-pad">
+				${button_array.map(get_row).join("")}
+			</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>`;
+		}
+	}
+
+	bind_events() {
+		// bind click event
+		const me = this;
+		this.wrapper.on('click', '.num-col', function() {
+			const $btn = $(this);
+			me.highlight_button($btn);
+			me.onclick.apply(null, [$btn.attr('data-value')]);
+		});
+	}
+
+	highlight_button($btn) {
+		// const $btn = this.wrapper.find(`[data-value="${value}"]`);
+		$btn.addClass('highlight');
+		setTimeout(() => $btn.removeClass('highlight'), 1000);
+	}
 }
\ No newline at end of file