From f2aa1764ccae6ee98805676bc40c16709acc012c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 20 May 2016 23:55:45 +0530 Subject: [PATCH] [POS] enhancement and minor fixes --- .../doctype/pos_profile/pos_profile.js | 12 -- .../doctype/pos_profile/pos_profile.json | 29 +--- .../doctype/pos_profile/pos_profile.py | 2 +- erpnext/accounts/doctype/sales_invoice/pos.py | 20 ++- .../sales_invoice/test_sales_invoice.py | 1 - erpnext/accounts/page/pos/pos.js | 127 +++++++++++++----- .../point_of_sale/point_of_sale.json | 4 +- erpnext/public/js/pos/pos_bill_item.html | 3 +- erpnext/public/js/pos/pos_invoice_list.html | 4 +- erpnext/public/less/erpnext.less | 52 +++++++ 10 files changed, 169 insertions(+), 85 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 8de8e351bc..99438a6a5f 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -24,18 +24,6 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { }); }); -//cash bank account -//------------------------------------ -cur_frm.fields_dict['cash_bank_account'].get_query = function(doc,cdt,cdn) { - return{ - filters:{ - 'report_type': "Balance Sheet", - 'is_group': 0, - 'company': doc.company - } - } -} - // Income Account // -------------------------------- cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) { diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 8f25ba90b3..b1420fc2a1 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -739,33 +739,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "cash_bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Cash/Bank Account", - "length": 0, - "no_copy": 0, - "oldfieldname": "cash_bank_account", - "oldfieldtype": "Link", - "options": "Account", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -857,7 +830,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-05-09 00:00:30.610878", + "modified": "2016-05-25 15:00:09.335025", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py index 634d63c190..5f4d5bc75d 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py @@ -27,7 +27,7 @@ class POSProfile(Document): self.company), raise_exception=1) def validate_all_link_fields(self): - accounts = {"Account": [self.cash_bank_account, self.income_account, + accounts = {"Account": [self.income_account, self.expense_account], "Cost Center": [self.cost_center], "Warehouse": [self.warehouse]} diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 4ec2524529..2fbf4b6ac8 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -7,6 +7,7 @@ from frappe import _ from frappe.utils import nowdate from erpnext.setup.utils import get_exchange_rate from erpnext.stock.get_item_details import get_pos_profile +from erpnext.accounts.party import get_party_account_currency from erpnext.controllers.accounts_controller import get_taxes_and_charges @frappe.whitelist() @@ -28,7 +29,7 @@ def get_pos_data(): return { 'doc': doc, 'items': get_items(doc, pos_profile), - 'customers': get_customers(pos_profile), + 'customers': get_customers(pos_profile, doc), 'pricing_rules': get_pricing_rules(doc), 'mode_of_payment': get_mode_of_payment(doc), 'print_template': print_template, @@ -68,6 +69,12 @@ def update_multi_mode_option(doc, pos_profile): from frappe.model import default_fields if not pos_profile: + for payment in frappe.get_all('Mode of Payment Account', fields=["default_account", "parent"], + filters = {'company': doc.company}): + payments = doc.append('payments', {}) + payments.mode_of_payment = payment.parent + payments.account = payment.default_account + return for payment_mode in pos_profile.payments: @@ -106,12 +113,19 @@ def get_items(doc, pos_profile): return item_list -def get_customers(pos_profile): +def get_customers(pos_profile, doc): filters = {'disabled': 0} + customer_list = [] if pos_profile.get('customer'): filters.update({'name': pos_profile.customer}) - return frappe.get_all("Customer", fields=["*"], filters = filters) + customers = frappe.get_all("Customer", fields=["*"], filters = filters) + + for customer in customers: + customer_currency = get_party_account_currency('Customer', customer.name, doc.company) or doc.currency + if customer_currency == doc.currency: + customer_list.append(customer) + return customer_list def get_pricing_rules(doc): if doc.ignore_pricing_rule == 0: diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index abc4083f1c..cc3d95227c 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -525,7 +525,6 @@ class TestSalesInvoice(unittest.TestCase): def make_pos_profile(self): pos_profile = frappe.get_doc({ - "cash_bank_account": "_Test Bank - _TC", "company": "_Test Company", "cost_center": "_Test Cost Center - _TC", "currency": "INR", diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 386ed9ed93..4b3da285f3 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -4,7 +4,7 @@ frappe.provide("erpnext.pos"); frappe.pages['pos'].on_page_load = function(wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, - title: 'Point of Sale', + title: __('Point of Sale'), single_column: true }); @@ -32,6 +32,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ var me = this; if(this.load){ this.load = false; + }else if(this.connection_status){ + this.onload(); }else{ this.create_new(); } @@ -42,12 +44,13 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ //Check Internet connection after every 30 seconds setInterval(function(){ me.set_indicator(); - }, 30000) + }, 5000) }, set_indicator: function(){ var me = this; // navigator.onLine + this.connection_status = false; this.page.set_indicator("Offline", "grey") frappe.call({ method:"frappe.handler.ping", @@ -73,6 +76,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ var me = this; this.page.add_menu_item(__("New Sales Invoice"), function() { + me.save_previous_entry() me.create_new() }) @@ -89,7 +93,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); this.page.add_menu_item(__("POS Profile"), function() { - frappe.set_route('POS Profile'); + frappe.set_route('List', 'POS Profile'); }); }, @@ -106,8 +110,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ $(this.list_body).append('
\
Sr
\
Customer
\ -
Grand Total
\ -
Status
\ +
Grand Total
\ +
Status
\
') $.each(this.si_docs, function(index, data){ @@ -117,7 +121,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ name: key, customer: data[key].customer, grand_total: format_currency(data[key].grand_total, me.frm.doc.currency), - status: (data[key].docstatus == 1) ? 'Submitted' : 'Draft' + data: me.get_doctype_status(data[key]) })).appendTo($(me.list_body)); } }) @@ -135,6 +139,16 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }) }, + get_doctype_status: function(doc){ + if(doc.outstanding_amount == 0){ + return {status: "Paid", indicator: "green"} + }else if(doc.docstatus == 0){ + return {status: "Draft", indicator: "red"} + }else if(doc.paid_amount >= 0){ + return {status: "Unpaid", indicator: "orange"} + } + }, + set_missing_values: function(){ var me = this; doc = JSON.parse(localStorage.getItem('doc')) @@ -174,11 +188,16 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }) }, + save_previous_entry : function(){ + if(this.frm.doc.items.length > 0){ + this.create_invoice() + } + }, + create_new: function(){ var me = this; this.frm = {} this.name = ''; - this.frm.doc = JSON.parse(localStorage.getItem('doc')) this.load_data(); this.setup(); }, @@ -187,6 +206,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.items = window.items; this.customers = window.customers; this.pricing_rules = window.pricing_rules; + this.frm.doc = JSON.parse(localStorage.getItem('doc')); $.each(window.meta, function(i, data){ frappe.meta.sync(data) @@ -264,6 +284,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ } this.party_field.$input.autocomplete({ + autoFocus: true, source: function (request, response) { me.customer_data = me.get_customers(request.term) response($.map(me.customer_data, function(data){ @@ -282,19 +303,30 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ } me.refresh(); } - }) + }).on("focus", function(){ + setTimeout(function() { + if(!me.party_field.$input.val()) { + me.party_field.$input.autocomplete( "search", " " ); + } + }, 500); + }); }, get_customers: function(key){ var me = this; - key = key.toLowerCase() - return $.grep(this.customers, function(data) { - if(data.name.toLowerCase().match(key) - || data.customer_name.toLowerCase().match(key) - || (data.customer_group && data.customer_group.toLowerCase().match(key))){ - return data - } - }) + key = key.toLowerCase().trim() + if(key){ + return $.grep(this.customers, function(data) { + if(data.name.toLowerCase().match(key) + || data.customer_name.toLowerCase().match(key) + || (data.customer_group && data.customer_group.toLowerCase().match(key))){ + return data + } + }) + }else{ + customers = this.customers.sort(function(a,b){ return a.idx < b.idx }) + return customers.slice(0, 20) + } }, make_item_list: function() { @@ -377,39 +409,52 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ $(this.wrapper).find(".pos-item-qty").on("change", function(){ var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - me.update_qty_against_item_code(item_code, $(this).val()); + me.update_qty_rate_against_item_code(item_code, "qty", $(this).val()); }) $(this.wrapper).find("[data-action='increase-qty']").on("click", function(){ var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1; - me.update_qty_against_item_code(item_code, qty); + me.update_qty_rate_against_item_code(item_code, "qty", qty); }) $(this.wrapper).find("[data-action='decrease-qty']").on("click", function(){ var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1; - me.update_qty_against_item_code(item_code, qty); + me.update_qty_rate_against_item_code(item_code, "qty", qty); }) }, - update_qty_against_item_code: function(item_code, qty){ + update_rate: function() { var me = this; - if(qty < 0){ - frappe.throw(__("Quantity must be positive")); + + $(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()); + }) + }, + + update_qty_rate_against_item_code: function(item_code, field, value){ + var me = this; + if(value < 0){ + frappe.throw(__("Enter value must be positive")); } this.remove_item = [] $.each(this.frm.doc["items"] || [], function(i, d) { if (d.item_code == item_code) { - d.qty = flt(qty); + d[field] = flt(value); d.amount = flt(d.rate) * flt(d.qty); if(d.qty==0){ me.remove_item.push(d.idx) } } }); - this.remove_zero_qty_item(); + + if(field == 'qty'){ + this.remove_zero_qty_item(); + } + this.refresh(); }, @@ -512,6 +557,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ var me = this; this.refresh_fields(); this.update_qty(); + this.update_rate(); this.set_primary_action(); }, refresh_fields: function() { @@ -544,7 +590,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ qty: d.qty, actual_qty: d.actual_qty, projected_qty: d.projected_qty, - rate: format_currency(d.rate, me.frm.doc.currency), + rate: format_number(d.rate, me.frm.doc.currency), amount: format_currency(d.amount, me.frm.doc.currency) })).appendTo($items); }); @@ -552,6 +598,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.wrapper.find("input.pos-item-qty").on("focus", function() { $(this).select(); }); + + this.wrapper.find("input.pos-item-rate").on("focus", function() { + $(this).select(); + }); }, set_taxes: function(){ @@ -596,18 +646,23 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }else if(this.frm.doc.docstatus == 1){ this.page.set_primary_action(__("Print"), function() { html = frappe.render(me.print_template, me.frm.doc) - frappe.require("/assets/js/print_format_v3.min.js", function() { - w = _p.preview(html); - setTimeout(function(){ - w.print(); - }, 1000) - }); + me.print_document(html) }) }else { this.page.clear_primary_action() } }, + print_document: function(html){ + var w = window.open(); + w.document.write(html); + w.document.close(); + setTimeout(function(){ + w.print(); + w.close(); + }, 1000) + }, + write_off_amount: function(){ var me = this; var value = 0.0; @@ -723,7 +778,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ var me = this; this.si_docs = this.get_submitted_invoice() - if(this.connection_status && this.si_docs.length){ + if(this.si_docs.length){ frappe.call({ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", args: { @@ -740,12 +795,14 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }, get_submitted_invoice: function(){ - invoices = [] - docs = this.get_doc_from_localstorage() + var invoices = []; + var index = 1; + docs = this.get_doc_from_localstorage(); if(docs){ invoices = $.map(docs, function(data){ for(key in data){ - if(data[key].docstatus == 1){ + if(data[key].docstatus == 1 && index < 50){ + index++ return data } } diff --git a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json index 7641924f02..645fce6fe6 100644 --- a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json +++ b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json @@ -6,9 +6,9 @@ "docstatus": 0, "doctype": "Print Format", "font": "Default", - "html": "\n\n

\n\t{{ company }}
\n\t{{ __(\"Invoice\") }}
\n

\n

\n\t{{ __(\"Date\") }}: {{ posting_date }}
\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
{{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
\n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ item.qty }}
@ {{ format_currency(item.rate, currency) }}
{{ format_currency(item.amount, currency) }}
\n\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
\n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
\n\n\n
\n

{{ __(\"Thank you, please visit again.\") }}

", + "html": "\n\n

\n\t{{ company }}
\n\t{{ __(\"Invoice\") }}
\n

\n

\n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
{{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
\n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ item.qty }}
@ {{ format_currency(item.rate, currency) }}
{{ format_currency(item.amount, currency) }}
\n\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
\n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
\n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
\n\n\n
\n

{{ __(\"Thank you, please visit again.\") }}

", "idx": 0, - "modified": "2016-05-11 15:04:24.359583", + "modified": "2016-05-21 00:25:20.359074", "modified_by": "Administrator", "name": "Point of Sale", "owner": "Administrator", diff --git a/erpnext/public/js/pos/pos_bill_item.html b/erpnext/public/js/pos/pos_bill_item.html index fe521f6fb9..c21e1d5ce8 100644 --- a/erpnext/public/js/pos/pos_bill_item.html +++ b/erpnext/public/js/pos/pos_bill_item.html @@ -17,6 +17,7 @@
-
{%= amount %}
{%= rate %}
+
+

{%= amount %}

diff --git a/erpnext/public/js/pos/pos_invoice_list.html b/erpnext/public/js/pos/pos_invoice_list.html index 8d2644ce27..cb25072756 100644 --- a/erpnext/public/js/pos/pos_invoice_list.html +++ b/erpnext/public/js/pos/pos_invoice_list.html @@ -1,6 +1,6 @@
{%= sr %}
{%= customer %}
-
{%= grand_total %}
-
{%= status %}
+
{%= grand_total %}
+
{{ data.status }}
diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index 58e8e6265d..7da54e7dd8 100644 --- a/erpnext/public/less/erpnext.less +++ b/erpnext/public/less/erpnext.less @@ -156,3 +156,55 @@ .dashboard-list-item:last-child { border-bottom: none; } + +.payment-toolbar { + margin-left: 35px; +} + +.payment-mode { + cursor: pointer; + font-family: sans-serif; + font-size: 15px; +} + +.pos-payment-row .col-xs-6 { + padding :10px; +} + +.pos-payment-row { + border-bottom:1px solid #d1d8dd; + margin: 2px 0px 5px 0px; +} + +.pos-payment-row:hover, .pos-keyboard-key:hover{ + background-color: #FAFBFC; + cursor: pointer; +} + +.pos-keyboard-key, .delete-btn { + border: 1px solid #d1d8dd; + height:85px; + width:85px; + margin:10px 10px; + font-size:24px; + font-weight:200; + background-color: #FDFDFD; + border-color: #e8e8e8; +} + +.amount { + margin-top: 5px; +} + +.amount-label { + font-size: 16px; +} + +.selected-payment-mode { + background-color: #FAFBFC; + cursor: pointer; +} + +.pos-invoice-list { + padding: 15px 10px; +}