diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index da49036973..8de8e351bc 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -3,11 +3,11 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { frm.set_query("selling_price_list", function() { - return { filter: { selling: 1 } }; + return { filters: { selling: 1 } }; }); frm.set_query("print_format", function() { - return { filter: { doc_type: "Sales Invoice" } }; + return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} }; }); erpnext.queries.setup_queries(frm, "Warehouse", function() { diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 0cf17da1c9..9bea107423 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -14,14 +14,16 @@ def get_pos_data(): doc = frappe.new_doc('Sales Invoice') doc.update_stock = 1; doc.is_pos = 1; - pos_profile = get_pos_profile(doc.company) + pos_profile = get_pos_profile(doc.company) or {} - if not pos_profile: - frappe.throw(_("Create pos profile first")) + if pos_profile.get('name'): + pos_profile = frappe.get_doc('POS Profile', pos_profile.get('name')) + else: + frappe.msgprint(_("Warning Message: Create pos profile")) - pos_profile = frappe.get_doc('POS Profile', pos_profile.name) update_pos_profile_data(doc, pos_profile) update_multi_mode_option(doc, pos_profile) + print_template = frappe.db.get_value('Print Format', pos_profile.get('print_format'), 'html') or '' return { 'doc': doc, @@ -29,7 +31,7 @@ def get_pos_data(): 'customers': get_customers(pos_profile), 'pricing_rules': get_pricing_rules(doc), 'mode_of_payment': get_mode_of_payment(doc), - 'print_template': frappe.db.get_value('Print Format', pos_profile.print_format, 'html') or '', + 'print_template': print_template, 'meta': { 'invoice': frappe.get_meta('Sales Invoice'), 'items': frappe.get_meta('Sales Invoice Item'), @@ -40,21 +42,21 @@ def get_pos_data(): def update_pos_profile_data(doc, pos_profile): company_data = frappe.db.get_value('Company', doc.company, '*', as_dict=1) - doc.taxes_and_charges = pos_profile.taxes_and_charges + doc.taxes_and_charges = pos_profile.get('taxes_and_charges') if doc.taxes_and_charges: update_tax_table(doc) - doc.currency = pos_profile.currency or company_data.default_currency + doc.currency = pos_profile.get('currency') or company_data.default_currency doc.conversion_rate = 1.0 if doc.currency != company_data.default_currency: doc.conversion_rate = get_exchange_rate(doc.currency, company_data.default_currency) - doc.selling_price_list = pos_profile.selling_price_list or frappe.db.get_value('Selling Settings', None, 'selling_price_list') - doc.naming_series = pos_profile.naming_series or 'SINV-' - doc.letter_head = pos_profile.letter_head or company_data.default_letter_head - doc.ignore_pricing_rule = pos_profile.ignore_pricing_rule - doc.apply_discount_on = pos_profile.apply_discount_on - doc.customer_group = pos_profile.customer_group or get_root('Customer Group') - doc.territory = pos_profile.territory or get_root('Territory') + doc.selling_price_list = pos_profile.get('selling_price_list') or frappe.db.get_value('Selling Settings', None, 'selling_price_list') + doc.naming_series = pos_profile.get('naming_series') or 'SINV-' + doc.letter_head = pos_profile.get('letter_head') or company_data.default_letter_head + doc.ignore_pricing_rule = pos_profile.get('ignore_pricing_rule') or 0 + doc.apply_discount_on = pos_profile.get('apply_discount_on') or '' + doc.customer_group = pos_profile.get('customer_group') or get_root('Customer Group') + doc.territory = pos_profile.get('territory') or get_root('Territory') def get_root(table): root = frappe.db.sql(""" select name from `tab%(table)s` having @@ -64,6 +66,9 @@ def get_root(table): def update_multi_mode_option(doc, pos_profile): from frappe.model import default_fields + + if not pos_profile: + return for payment_mode in pos_profile.payments: payment_mode = payment_mode.as_dict() @@ -89,10 +94,10 @@ def get_items(doc, pos_profile): item.price_list_rate = frappe.db.get_value('Item Price', {'item_code': item.name, 'price_list': doc.selling_price_list}, 'price_list_rate') or 0 - item.default_warehouse = pos_profile.warehouse or item.default_warehouse or None - item.expense_account = pos_profile.expense_account or item.expense_account - item.income_account = pos_profile.income_account or item_doc.income_account - item.cost_center = pos_profile.cost_center or item_doc.selling_cost_center + item.default_warehouse = pos_profile.get('warehouse') or item.default_warehouse or None + item.expense_account = pos_profile.get('expense_account') or item.expense_account + item.income_account = pos_profile.get('income_account') or item_doc.income_account + item.cost_center = pos_profile.get('cost_center') or item_doc.selling_cost_center item.actual_qty = frappe.db.get_value('Bin', {'item_code': item.name, 'warehouse': item.default_warehouse}, 'actual_qty') or 0 item.serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where warehouse= %(warehouse)s @@ -103,7 +108,7 @@ def get_items(doc, pos_profile): def get_customers(pos_profile): filters = {'disabled': 0} - if pos_profile.customer: + if pos_profile.get('customer'): filters.update({'name': pos_profile.customer}) return frappe.get_all("Customer", fields=["*"], filters = filters) @@ -133,13 +138,13 @@ def make_invoice(doc_list): si_doc = frappe.new_doc('Sales Invoice') si_doc.offline_pos_name = name si_doc.update(doc) - submit_invoice(si_doc) + submit_invoice(si_doc, name) name_list.append(name) return name_list def validate_customer(doc): - if not frappe.db.get_value('Customer', doc.get('customer')): + if not frappe.db.exists('Customer', doc.get('customer')): customer_doc = frappe.new_doc('Customer') customer_doc.customer_name = doc.get('customer') customer_doc.customer_type = 'Company' @@ -165,20 +170,21 @@ def validate_item(doc): item_doc.save(ignore_permissions=True) frappe.db.commit() -def submit_invoice(si_doc): +def submit_invoice(si_doc, name): try: si_doc.insert() si_doc.submit() except Exception, e: if frappe.message_log: frappe.message_log.pop() frappe.db.rollback() - save_invoice(e, si_doc) + save_invoice(e, si_doc, name) -def save_invoice(e, si_doc): - si_doc.docstatus = 0 - si_doc.name = '' - si_doc.save(ignore_permissions=True) - make_scheduler_log(e, si_doc.name) +def save_invoice(e, si_doc, name): + if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name, 'docstatus': 1}): + si_doc.docstatus = 0 + si_doc.name = '' + si_doc.save(ignore_permissions=True) + make_scheduler_log(e, si_doc.name) def make_scheduler_log(e, sales_invoice): scheduler_log = frappe.new_doc('Scheduler Log') diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 04dd5804d2..2162563f22 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -7,21 +7,35 @@ frappe.pages['pos'].on_page_load = function(wrapper) { title: 'Point of Sale', single_column: true }); - - wrapper = $(wrapper).find('.page-content') - new erpnext.pos.PointOfSale(page, wrapper) + + wrapper.pos = new erpnext.pos.PointOfSale(wrapper) } +frappe.pages['pos'].refresh = function(wrapper) { + wrapper.pos.on_refresh_page() +} + + erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ - init: function(page, wrapper){ - this.page = page; - this.wrapper = wrapper; + init: function(wrapper){ + this.load = true; + this.page = wrapper.page; + this.wrapper = $(wrapper).find('.page-content'); this.set_indicator(); this.onload(); this.make_menu_list(); this.set_interval_for_si_sync(); this.si_docs = this.get_doc_from_localstorage(); }, + + on_refresh_page: function() { + var me = this; + if(this.load){ + this.load = false; + }else{ + this.create_new(); + } + }, check_internet_connection: function(){ var me = this; @@ -48,7 +62,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ onload: function(){ var me = this; - this.get_data_from_server(function(){ me.create_new(); }); @@ -64,7 +77,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); this.page.add_menu_item(__("Sync Master Data"), function(){ - me.get_data_from_server() + me.get_data_from_server(function(){ + me.load_data() + me.make_customer() + me.make_item_list() + }) }); this.page.add_menu_item(__("New Sales Invoice"), function() { @@ -83,7 +100,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.list_dialog.show(); this.list_body = this.list_dialog.body; $(this.list_body).append('
\ -
Customer
\ +
Sr
\ +
Customer
\
Grand Total
\
Status
\
') @@ -91,9 +109,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ $.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, - grand_total: data[key].grand_total, + grand_total: format_currency(data[key].grand_total, me.frm.doc.currency), status: (data[key].docstatus == 1) ? 'Submitted' : 'Draft' })).appendTo($(me.list_body)); } @@ -104,12 +123,21 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ doc_data = me.get_invoice_doc(me.si_docs) if(doc_data){ me.frm.doc = doc_data[0][me.name]; + me.set_missing_values(); me.refresh(); me.disable_input_field(); me.list_dialog.hide(); } }) }, + + set_missing_values: function(){ + var me = this; + doc = JSON.parse(localStorage.getItem('doc')) + if(this.frm.doc.payments.length == 0){ + this.frm.doc.payments = doc.payments; + } + }, get_invoice_doc: function(si_docs){ var me = this; @@ -127,6 +155,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ frappe.call({ method: "erpnext.accounts.doctype.sales_invoice.pos.get_pos_data", freeze: true, + freeze_message: __("Master data syncing, it might take some time"), callback: function(r){ window.items = r.message.items; window.customers = r.message.customers; @@ -142,8 +171,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }, create_new: function(){ - this.frm = {} var me = this; + this.frm = {} this.name = ''; this.frm.doc = JSON.parse(localStorage.getItem('doc')) this.load_data(); @@ -555,11 +584,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ me.create_invoice(); me.make_payment(); }); - }else if(this.frm.doc.docstatus == 0){ + }else if(this.frm.doc.docstatus == 0 && this.name){ this.page.set_primary_action(__("Submit"), function() { - frappe.confirm(__("Do you really want to submit the invoice?"), function () { - me.write_off_amount() - }) + me.write_off_amount() }) }else if(this.frm.doc.docstatus == 1){ this.page.set_primary_action(__("Print"), function() { @@ -571,6 +598,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }, 1000) }); }) + }else { + this.page.clear_primary_action() } }, @@ -582,27 +611,36 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ dialog = new frappe.ui.Dialog({ title: 'Write Off Amount', fields: [ - {fieldtype: "Currency", fieldname: "write_off_amount", label: __("Amount"), reqd: 1}, + {fieldtype: "Check", fieldname: "write_off_amount", label: __("Write of Outstanding Amount")}, ] - }); + }); - dialog.show(); + dialog.show(); - dialog.fields_dict.write_off_amount.$input.change(function(){ - value = dialog.get_values() - }) - - dialog.set_primary_action(__("Submit"), function(){ - me.frm.doc.write_off_amount = value.write_off_amount; - me.calculate_outstanding_amount(); - dialog.hide(); - me.change_status(); - }) + dialog.fields_dict.write_off_amount.$input.change(function(){ + write_off_amount = dialog.get_values().write_off_amount + me.frm.doc.write_off_outstanding_amount_automatically = write_off_amount; + me.frm.doc.base_write_off_amount = (write_off_amount==1) ? flt(me.frm.doc.grand_total - me.frm.doc.paid_amount, precision("outstanding_amount")) : 0; + me.frm.doc.write_off_amount = flt(me.frm.doc.base_write_off_amount * me.frm.doc.conversion_rate, precision("write_off_amount")) + me.calculate_outstanding_amount(); + }) + + dialog.set_primary_action(__("Submit"), function(){ + dialog.hide() + me.submit_invoice() + }) }else{ - me.change_status(); + this.submit_invoice() } }, + submit_invoice: function(){ + var me = this; + frappe.confirm(__("Do you really want to submit the invoice?"), function () { + me.change_status(); + }) + }, + change_status: function(){ if(this.frm.doc.docstatus == 0){ this.frm.doc.docstatus = 1; @@ -662,14 +700,18 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }, get_doc_from_localstorage: function(){ - return JSON.parse(localStorage.getItem('sales_invoice_doc')) || []; + try{ + return JSON.parse(localStorage.getItem('sales_invoice_doc')) || []; + }catch(e){ + return [] + } }, set_interval_for_si_sync: function(){ var me = this; setInterval(function(){ me.sync_sales_invoice() - }, 6000) + }, 60000) }, sync_sales_invoice: function(){ @@ -711,14 +753,16 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ remove_doc_from_localstorage: function(){ var me = this; this.si_docs = this.get_doc_from_localstorage(); + this.new_si_docs = [] if(this.removed_items){ $.each(this.si_docs, function(index, data){ for(key in data){ - if(in_list(me.removed_items, key)){ - me.si_docs.splice(index) + if(!in_list(me.removed_items, key)){ + me.new_si_docs.push(data) } } }) + this.si_docs = this.new_si_docs; this.update_localstorage(); } }, diff --git a/erpnext/accounts/print_format/point_of_sale/__init__.py b/erpnext/accounts/print_format/point_of_sale/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..7641924f02 --- /dev/null +++ b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json @@ -0,0 +1,18 @@ +{ + "creation": "2016-05-05 17:16:18.564460", + "custom_format": 1, + "disabled": 0, + "doc_type": "Sales Invoice", + "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.\") }}

", + "idx": 0, + "modified": "2016-05-11 15:04:24.359583", + "modified_by": "Administrator", + "name": "Point of Sale", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Js", + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py index 68bf7681e3..9944db9882 100644 --- a/erpnext/config/desktop.py +++ b/erpnext/config/desktop.py @@ -131,8 +131,7 @@ def get_data(): "icon": "octicon octicon-credit-card", "type": "page", "link": "pos", - "label": _("POS"), - "hidden": 1 + "label": _("POS") }, { "module_name": "Projects", diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 03c381a85e..340ebdb5e5 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -15,15 +15,13 @@ "public/js/templates/address_list.html", "public/js/templates/contact_list.html", "public/js/controllers/stock_controller.js", - "public/js/controllers/payments.js", + "public/js/payment/payments.js", "public/js/controllers/taxes_and_totals.js", "public/js/controllers/transaction.js", "public/js/pos/pos.html", "public/js/pos/pos_bill_item.html", "public/js/pos/pos_item.html", "public/js/pos/pos_tax_row.html", - "public/js/pos/pos_print.html", - "public/js/pos/pos.js", "public/js/pos/pos_invoice_list.html", "public/js/payment/pos_payment.html", "public/js/payment/payment_details.html", diff --git a/erpnext/public/css/erpnext.css b/erpnext/public/css/erpnext.css index 6777e1e4d7..346cfa0c62 100644 --- a/erpnext/public/css/erpnext.css +++ b/erpnext/public/css/erpnext.css @@ -124,3 +124,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; +} diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index c6224668bc..af651c9b85 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -165,7 +165,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ erpnext.hide_company(); this.show_item_wise_taxes(); this.set_dynamic_labels(); - // erpnext.pos.make_pos_btn(this.frm); this.setup_sms(); this.make_show_payments_btn(); }, diff --git a/erpnext/public/js/controllers/payments.js b/erpnext/public/js/payment/payments.js similarity index 93% rename from erpnext/public/js/controllers/payments.js rename to erpnext/public/js/payment/payments.js index 7ac2b7baea..f5527b1d04 100644 --- a/erpnext/public/js/controllers/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -20,10 +20,8 @@ erpnext.payments = erpnext.stock.StockController.extend({ var me = this; this.dialog.set_primary_action(__("Submit"), function() { - frappe.confirm(__("Do you really want to submit the invoice?"), function () { - me.write_off_amount(); - me.dialog.hide(); - }) + me.dialog.hide() + me.write_off_amount() }) }, @@ -35,13 +33,6 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.bind_keyboard_event() }, - pay_amount: function(){ - var me = this; - this.make_multimode_payment(); - this.calculate_outstanding_amount() - this.show_payment_details(); - }, - make_multimode_payment: function(){ var me = this; @@ -91,12 +82,12 @@ erpnext.payments = erpnext.stock.StockController.extend({ me.payment_val = flt(me.frm.doc.outstanding_amount) me.selected_mode.val(format_number(me.payment_val, 2)); me.update_paid_amount() - me.bind_amount_change_event(); }else if(flt(me.selected_mode.val()) > 0){ //If user click on existing row which has value me.payment_val = flt(me.selected_mode.val()); } me.selected_mode.select() + me.bind_amount_change_event(); }) }, diff --git a/erpnext/public/js/payment/pos_payment.html b/erpnext/public/js/payment/pos_payment.html index e93644dc28..223850c504 100644 --- a/erpnext/public/js/payment/pos_payment.html +++ b/erpnext/public/js/payment/pos_payment.html @@ -31,7 +31,7 @@ {% } %}
- +
diff --git a/erpnext/public/js/pos/pos.js b/erpnext/public/js/pos/pos.js deleted file mode 100644 index 60801e628a..0000000000 --- a/erpnext/public/js/pos/pos.js +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.provide("erpnext.pos"); - -erpnext.pos.PointOfSale = Class.extend({ - init: function(wrapper, frm) { - this.wrapper = wrapper; - this.frm = frm; - this.wrapper.html(frappe.render_template("pos", {currency: this.frm.currency})); - - this.check_transaction_type(); - this.make(); - - var me = this; - $(this.frm.wrapper).on("refresh-fields", function() { - me.refresh(); - }); - - this.wrapper.find('input.discount-percentage').on("change", function() { - frappe.model.set_value(me.frm.doctype, me.frm.docname, - "additional_discount_percentage", flt(this.value)); - }); - - this.wrapper.find('input.discount-amount').on("change", function() { - frappe.model.set_value(me.frm.doctype, me.frm.docname, "discount_amount", flt(this.value)); - }); - }, - check_transaction_type: function() { - var me = this; - - // Check whether the transaction is "Sales" or "Purchase" - if (frappe.meta.has_field(cur_frm.doc.doctype, "customer")) { - this.set_transaction_defaults("Customer"); - } - else if (frappe.meta.has_field(cur_frm.doc.doctype, "supplier")) { - this.set_transaction_defaults("Supplier"); - } - }, - set_transaction_defaults: function(party) { - var me = this; - this.party = party; - this.price_list = (party == "Customer" ? - this.frm.doc.selling_price_list : this.frm.doc.buying_price_list); - this.price_list_field = (party == "Customer" ? "selling_price_list" : "buying_price_list"); - this.sales_or_purchase = (party == "Customer" ? "Sales" : "Purchase"); - }, - make: function() { - this.make_party(); - this.make_search(); - this.make_item_list(); - }, - make_party: function() { - var me = this; - this.party_field = frappe.ui.form.make_control({ - df: { - "fieldtype": "Link", - "options": this.party, - "label": this.party, - "fieldname": this.party.toLowerCase(), - "placeholder": this.party - }, - parent: this.wrapper.find(".party-area"), - frm: this.frm, - doctype: this.frm.doctype, - docname: this.frm.docname, - only_input: true, - }); - this.party_field.make_input(); - this.party_field.$input.on("change", function() { - if(!me.party_field.autocomplete_open) - frappe.model.set_value(me.frm.doctype, me.frm.docname, - me.party.toLowerCase(), this.value); - }); - }, - make_search: function() { - var me = this; - this.search = frappe.ui.form.make_control({ - df: { - "fieldtype": "Data", - "label": "Item", - "fieldname": "pos_item", - "placeholder": "Search Item" - }, - parent: this.wrapper.find(".search-area"), - only_input: true, - }); - this.search.make_input(); - this.search.$input.on("keyup", function() { - if(!me.search.autocomplete_open) - if(me.item_timeout) - clearTimeout(me.item_timeout); - me.item_timeout = setTimeout(function() { me.make_item_list(); }, 1000); - }); - }, - make_item_list: function() { - var me = this; - if(!this.price_list) { - msgprint(__("Price List not found or disabled")); - return; - } - - me.item_timeout = null; - frappe.call({ - method: 'erpnext.accounts.doctype.sales_invoice.pos.get_items', - args: { - sales_or_purchase: this.sales_or_purchase, - price_list: this.price_list, - item: this.search.$input.val() - }, - callback: function(r) { - var $wrap = me.wrapper.find(".item-list"); - me.wrapper.find(".item-list").empty(); - if (r.message) { - if (r.message.length === 1) { - var item = r.message[0]; - if (item.serial_no) { - me.add_to_cart(item.item_code, item.serial_no); - me.search.$input.val(""); - return; - - } else if (item.barcode) { - me.add_to_cart(item.item_code); - me.search.$input.val(""); - return; - } - } - - $.each(r.message, function(index, obj) { - $(frappe.render_template("pos_item", { - item_code: obj.name, - item_price: format_currency(obj.price_list_rate, obj.currency), - item_name: obj.name===obj.item_name ? "" : obj.item_name, - item_image: obj.image ? "url('" + obj.image + "')" : null, - color: frappe.get_palette(obj.item_name), - abbr: frappe.get_abbr(obj.item_name) - })).tooltip().appendTo($wrap); - }); - } - - // if form is local then allow this function - $(me.wrapper).find("div.pos-item").on("click", function() { - if(me.frm.doc.docstatus==0) { - me.add_to_cart($(this).attr("data-item-code")); - } - }); - } - }); - }, - add_to_cart: function(item_code, serial_no) { - var me = this; - var caught = false; - - if(!me.frm.doc[me.party.toLowerCase()] && ((me.frm.doctype == "Quotation" && - me.frm.doc.quotation_to == "Customer") - || me.frm.doctype != "Quotation")) { - msgprint(__("Please select {0} first.", [me.party])); - return; - } - - // get no_of_items - var no_of_items = me.wrapper.find(".pos-bill-item").length; - - // check whether the item is already added - if (no_of_items != 0) { - $.each(this.frm.doc["items"] || [], function(i, d) { - if (d.item_code == item_code) { - caught = true; - if (serial_no) - frappe.model.set_value(d.doctype, d.name, "serial_no", d.serial_no + '\n' + serial_no); - else - frappe.model.set_value(d.doctype, d.name, "qty", d.qty + 1); - } - }); - } - - // if item not found then add new item - if (!caught) - this.add_new_item_to_grid(item_code, serial_no); - - this.refresh(); - this.refresh_search_box(); - }, - add_new_item_to_grid: function(item_code, serial_no) { - var me = this; - - var child = frappe.model.add_child(me.frm.doc, this.frm.doctype + " Item", "items"); - child.item_code = item_code; - child.qty = 1; - - if (serial_no) - child.serial_no = serial_no; - - this.frm.script_manager.trigger("item_code", child.doctype, child.name); - frappe.after_ajax(function() { - me.frm.script_manager.trigger("qty", child.doctype, child.name); - }) - }, - refresh_search_box: function() { - var me = this; - - // Clear Item Box and remake item list - if (this.search.$input.val()) { - this.search.set_input(""); - this.make_item_list(); - } - }, - update_qty: function(item_code, qty) { - var me = this; - $.each(this.frm.doc["items"] || [], function(i, d) { - if (d.item_code == item_code) { - if (qty == 0) { - frappe.model.clear_doc(d.doctype, d.name); - me.refresh_grid(); - } else { - frappe.model.set_value(d.doctype, d.name, "qty", qty); - } - } - }); - this.refresh(); - }, - refresh: function() { - var me = this; - - this.refresh_item_list(); - this.refresh_fields(); - - // if form is local then only run all these functions - if (this.frm.doc.docstatus===0) { - this.call_when_local(); - } - - this.disable_text_box_and_button(); - this.set_primary_action(); - - // If quotation to is not Customer then remove party - if (this.frm.doctype == "Quotation" && this.frm.doc.quotation_to!="Customer") { - this.party_field.$input.prop("disabled", true); - } - }, - refresh_fields: function() { - this.party_field.set_input(this.frm.doc[this.party.toLowerCase()]); - this.party_field.frm = this.frm; - this.party_field.doctype = this.frm.doctype; - this.party_field.docname = this.frm.docname; - - this.wrapper.find('input.discount-percentage').val(this.frm.doc.additional_discount_percentage); - this.wrapper.find('input.discount-amount').val(this.frm.doc.discount_amount); - - this.show_items_in_item_cart(); - this.show_taxes(); - this.set_totals(); - }, - refresh_item_list: function() { - var me = this; - // refresh item list on change of price list - if (this.frm.doc[this.price_list_field] != this.price_list) { - this.price_list = this.frm.doc[this.price_list_field]; - this.make_item_list(); - } - }, - show_items_in_item_cart: function() { - var me = this; - var $items = this.wrapper.find(".items").empty(); - - $.each(this.frm.doc.items|| [], function(i, d) { - $(frappe.render_template("pos_bill_item", { - item_code: d.item_code, - item_name: (d.item_name===d.item_code || !d.item_name) ? "" : ("
" + d.item_name), - qty: d.qty, - actual_qty: d.actual_qty, - projected_qty: d.projected_qty, - rate: format_currency(d.rate, me.frm.doc.currency), - amount: format_currency(d.amount, me.frm.doc.currency) - })).appendTo($items); - }); - - this.wrapper.find("input.pos-item-qty").on("focus", function() { - $(this).select(); - }); - }, - show_taxes: function() { - var me = this; - var taxes = this.frm.doc["taxes"] || []; - $(this.wrapper) - .find(".tax-area").toggleClass("hide", (taxes && taxes.length) ? false : true) - .find(".tax-table").empty(); - - $.each(taxes, function(i, d) { - if (d.tax_amount) { - $(frappe.render_template("pos_tax_row", { - description: d.description, - tax_amount: format_currency(flt(d.tax_amount)/flt(me.frm.doc.conversion_rate), - me.frm.doc.currency) - })).appendTo(me.wrapper.find(".tax-table")); - } - }); - }, - set_totals: function() { - var me = this; - this.wrapper.find(".net-total").text(format_currency(me.frm.doc["net_total"], me.frm.doc.currency)); - this.wrapper.find(".grand-total").text(format_currency(me.frm.doc.grand_total, me.frm.doc.currency)); - }, - call_when_local: function() { - var me = this; - - // append quantity to the respective item after change from input box - $(this.wrapper).find("input.pos-item-qty").on("change", function() { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - me.update_qty(item_code, $(this).val()); - }); - - // increase/decrease qty on plus/minus button - $(this.wrapper).find(".pos-qty-btn").on("click", function() { - var $item = $(this).parents(".pos-bill-item:first"); - me.increase_decrease_qty($item, $(this).attr("data-action")); - }); - - this.focus(); - }, - focus: function() { - if(this.frm.doc[this.party.toLowerCase()]) { - this.search.$input.focus(); - } else { - if(!(this.frm.doctype == "Quotation" && this.frm.doc.quotation_to!="Customer")) - this.party_field.$input.focus(); - } - }, - increase_decrease_qty: function($item, operation) { - var item_code = $item.attr("data-item-code"); - var item_qty = cint($item.find("input.pos-item-qty").val()); - - if (operation == "increase-qty") - this.update_qty(item_code, item_qty + 1); - else if (operation == "decrease-qty" && item_qty != 0) - this.update_qty(item_code, item_qty - 1); - }, - disable_text_box_and_button: function() { - var me = this; - // if form is submitted & cancelled then disable all input box & buttons - $(this.wrapper) - .find(".pos-qty-btn") - .toggle(this.frm.doc.docstatus===0); - - $(this.wrapper).find('input, button').prop("disabled", !(this.frm.doc.docstatus===0)); - - this.wrapper.find(".pos-item-area").toggleClass("hide", me.frm.doc.docstatus!==0); - - }, - set_primary_action: function() { - var me = this; - if (this.frm.page.current_view_name==="main") return; - - if (this.frm.doctype == "Sales Invoice" && this.frm.doc.docstatus===0) { - if (!this.frm.doc.is_pos) { - this.frm.set_value("is_pos", 1); - } - this.frm.page.set_primary_action(__("Pay"), function() { - me.make_payment(); - }); - } else if (this.frm.doc.docstatus===1) { - this.frm.page.set_primary_action(__("New"), function() { - erpnext.open_as_pos = true; - new_doc(me.frm.doctype); - }); - } - }, - refresh_delete_btn: function() { - $(this.wrapper).find(".remove-items").toggle($(".item-cart .warning").length ? true : false); - }, - remove_selected_items: function() { - var me = this; - var selected_items = []; - var no_of_items = $(this.wrapper).find("#cart tbody tr").length; - for(var x=0; x<=no_of_items - 1; x++) { - var row = $(this.wrapper).find("#cart tbody tr:eq(" + x + ")"); - if(row.attr("data-selected") == "true") { - selected_items.push(row.attr("id")); - } - } - - var child = this.frm.doc["items"] || []; - - $.each(child, function(i, d) { - for (var i in selected_items) { - if (d.item_code == selected_items[i]) { - frappe.model.clear_doc(d.doctype, d.name); - } - } - }); - - this.refresh_grid(); - }, - refresh_grid: function() { - this.frm.dirty(); - this.frm.fields_dict["items"].grid.refresh(); - this.frm.script_manager.trigger("calculate_taxes_and_totals"); - this.refresh(); - }, - with_modes_of_payment: function(callback) { - var me = this; - if(me.modes_of_payment) { - callback(); - } else { - me.modes_of_payment = []; - $.ajax("/api/resource/Mode of Payment").success(function(data) { - $.each(data.data, function(i, d) { me.modes_of_payment.push(d.name); }); - callback(); - }); - } - }, - make_payment: function() { - var me = this; - var no_of_items = this.frm.doc.items.length; - - if (no_of_items == 0) - msgprint(__("Payment cannot be made for empty cart")); - else { - - this.with_modes_of_payment(function() { - // prefer cash payment! - var default_mode = me.frm.doc.mode_of_payment ? me.frm.doc.mode_of_payment : - me.modes_of_payment.indexOf(__("Cash"))!==-1 ? __("Cash") : undefined; - - // show payment wizard - var dialog = new frappe.ui.Dialog({ - width: 400, - title: 'Payment', - fields: [ - {fieldtype:'Currency', - fieldname:'total_amount', label: __('Total Amount'), - "default": me.frm.doc.grand_total}, - {fieldtype:'Select', fieldname:'mode_of_payment', - label: __('Mode of Payment'), - options: me.modes_of_payment.join('\n'), reqd: 1, - "default": default_mode}, - {fieldtype:'Currency', fieldname:'paid_amount', label:__('Amount Paid'), - reqd:1, "default": me.frm.doc.grand_total, - change: function() { - var values = dialog.get_values(); - - var actual_change = flt(values.paid_amount - values.total_amount, - precision("paid_amount")); - - if (actual_change > 0) { - var rounded_change = - round_based_on_smallest_currency_fraction(actual_change, - me.frm.doc.currency, precision("paid_amount")); - } else { - var rounded_change = 0; - } - - dialog.set_value("change", rounded_change); - dialog.get_input("change").trigger("change"); - - }}, - {fieldtype:'Currency', fieldname:'change', label: __('Change'), - "default": 0.0, hidden: 1, change: function() { - var values = dialog.get_values(); - var write_off_amount = (flt(values.paid_amount) - flt(values.change)) - values.total_amount; - dialog.get_field("write_off_amount").toggle(write_off_amount); - dialog.set_value("write_off_amount", write_off_amount); - } - }, - {fieldtype:'Currency', fieldname:'write_off_amount', - label: __('Write Off'), "default": 0.0, hidden: 1}, - ] - }); - me.dialog = dialog; - dialog.show(); - - // make read only - dialog.get_input("total_amount").prop("disabled", true); - dialog.get_input("write_off_amount").prop("disabled", true); - - // toggle amount paid and change - dialog.get_input("mode_of_payment").on("change", function() { - var is_cash = dialog.get_value("mode_of_payment") === __("Cash"); - dialog.get_field("paid_amount").toggle(is_cash); - dialog.get_field("change").toggle(is_cash); - - if (is_cash && !dialog.get_value("change")) { - // set to nearest 5 - dialog.set_value("paid_amount", dialog.get_value("total_amount")); - dialog.get_input("paid_amount").trigger("change"); - } else if (!is_cash) { - dialog.set_value("paid_amount", dialog.get_value("total_amount")); - dialog.set_value("change", 0); - } - }).trigger("change"); - - me.set_pay_button(dialog); - }); - } - }, - set_pay_button: function(dialog) { - var me = this; - dialog.set_primary_action(__("Pay"), function() { - var values = dialog.get_values(); - var is_cash = values.mode_of_payment === __("Cash"); - if (!is_cash) { - values.write_off_amount = values.change = 0.0; - values.paid_amount = values.total_amount; - } - me.frm.set_value("mode_of_payment", values.mode_of_payment); - - var paid_amount = flt((flt(values.paid_amount) - flt(values.change)), precision("paid_amount")); - me.frm.set_value("paid_amount", paid_amount); - - // specifying writeoff amount here itself, so as to avoid recursion issue - me.frm.set_value("write_off_amount", me.frm.doc.grand_total - paid_amount); - me.frm.set_value("outstanding_amount", 0); - - me.frm.savesubmit(this); - dialog.hide(); - }) - - } -}); - -erpnext.pos.make_pos_btn = function(frm) { - frm.page.add_menu_item(__("{0} View", [frm.page.current_view_name === "pos" ? "Form" : "Point-of-Sale"]), function() { - erpnext.pos.toggle(frm); - }); - - if(frm.pos_btn) return; - - // Show POS button only if it is enabled from features setup - if (cint(sys_defaults.fs_pos_view)!==1 || frm.doctype==="Material Request") { - return; - } - - if(!frm.pos_btn) { - frm.pos_btn = frm.page.add_action_icon("icon-th", function() { - erpnext.pos.toggle(frm); - }); - } - - if(erpnext.open_as_pos && frm.page.current_view_name !== "pos") { - erpnext.pos.toggle(frm, true); - } -} - -erpnext.pos.toggle = function(frm, show) { - // Check whether it is Selling or Buying cycle - var price_list = frappe.meta.has_field(cur_frm.doc.doctype, "selling_price_list") ? - frm.doc.selling_price_list : frm.doc.buying_price_list; - - if(show!==undefined) { - if((show===true && frm.page.current_view_name === "pos") - || (show===false && frm.page.current_view_name === "main")) { - return; - } - } - - if(frm.page.current_view_name!=="pos") { - // before switching, ask for pos name - if(!price_list) { - frappe.throw(__("Please select Price List")); - } - - if(!frm.doc.company) { - frappe.throw(__("Please select Company")); - } - } - - // make pos - if(!frm.pos) { - var wrapper = frm.page.add_view("pos", "
"); - frm.pos = new erpnext.pos.PointOfSale(wrapper, frm); - } - - // toggle view - frm.page.set_view(frm.page.current_view_name==="pos" ? "main" : "pos"); - - frm.toolbar.current_status = null; - frm.refresh(); - - // refresh - if(frm.page.current_view_name==="pos") { - frm.pos.refresh(); - } -} diff --git a/erpnext/public/js/pos/pos_invoice_list.html b/erpnext/public/js/pos/pos_invoice_list.html index 463b3e7e5f..8d2644ce27 100644 --- a/erpnext/public/js/pos/pos_invoice_list.html +++ b/erpnext/public/js/pos/pos_invoice_list.html @@ -1,5 +1,6 @@
-
{%= customer %}
+
{%= sr %}
+
{%= customer %}
{%= grand_total %}
{%= status %}
diff --git a/erpnext/public/js/pos/pos_print.html b/erpnext/public/js/pos/pos_print.html deleted file mode 100644 index d31a88568f..0000000000 --- a/erpnext/public/js/pos/pos_print.html +++ /dev/null @@ -1,25 +0,0 @@ - - -

- {{ company }}
-

-

- {{currency}} -

-{% for item in items %} -

{{item.item_code}}

-{% endfor %} \ No newline at end of file