diff --git a/erpnext/accounts/doctype/pos_setting/pos_setting.js b/erpnext/accounts/doctype/pos_setting/pos_setting.js index e2fe88896e..6f7fcd2b78 100755 --- a/erpnext/accounts/doctype/pos_setting/pos_setting.js +++ b/erpnext/accounts/doctype/pos_setting/pos_setting.js @@ -78,3 +78,24 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) cur_frm.fields_dict.user.get_query = function(doc,cdt,cdn) { return{ query:"frappe.core.doctype.user.user.user_query"} } + +cur_frm.fields_dict.write_off_account.get_query = function(doc) { + return{ + filters:{ + 'report_type': 'Profit and Loss', + 'group_or_ledger': 'Ledger', + 'company': doc.company + } + } +} + +// Write off cost center +//----------------------- +cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) { + return{ + filters:{ + 'group_or_ledger': 'Ledger', + 'company': doc.company + } + } +} diff --git a/erpnext/accounts/doctype/pos_setting/pos_setting.json b/erpnext/accounts/doctype/pos_setting/pos_setting.json index 10bd1685a3..420263b3d2 100755 --- a/erpnext/accounts/doctype/pos_setting/pos_setting.json +++ b/erpnext/accounts/doctype/pos_setting/pos_setting.json @@ -170,6 +170,24 @@ "permlevel": 0, "read_only": 0 }, + { + "fieldname": "write_off_account", + "fieldtype": "Link", + "label": "Write Off Account", + "options": "Account", + "permlevel": 0, + "precision": "", + "reqd": 1 + }, + { + "fieldname": "write_off_cost_center", + "fieldtype": "Link", + "label": "Write Off Cost Center", + "options": "Cost Center", + "permlevel": 0, + "precision": "", + "reqd": 1 + }, { "allow_on_submit": 1, "fieldname": "letter_head", @@ -207,7 +225,7 @@ ], "icon": "icon-cog", "idx": 1, - "modified": "2015-01-01 14:30:03.415900", + "modified": "2015-01-06 02:20:25.431768", "modified_by": "Administrator", "module": "Accounts", "name": "POS Setting", diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 986568da29..36d404489a 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -42,22 +42,6 @@ def get_items(price_list, sales_or_purchase, item=None): where %s""" % ('%(price_list)s', condition), args, as_dict=1) -# @frappe.whitelist() -# def get_item_code(barcode_serial_no): -# input_via = "serial_no" -# item_code = frappe.db.sql("""select name, item_code from `tabSerial No` where -# name=%s""", (barcode_serial_no), as_dict=1) -# -# if not item_code: -# input_via = "barcode" -# item_code = frappe.db.sql("""select name from `tabItem` where barcode=%s""", -# (barcode_serial_no), as_dict=1) -# -# if item_code: -# return item_code, input_via -# else: -# frappe.throw(frappe._("Invalid Barcode or Serial No")) - @frappe.whitelist() def get_mode_of_payment(): - return frappe.get_list("Mode of Payment") + return sorted([d.name for d in frappe.get_list("Mode of Payment")]) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index a040438c26..ff39939a1b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -246,6 +246,7 @@ cur_frm.cscript.hide_fields = function(doc) { cur_frm.cscript.mode_of_payment = function(doc) { + console.log("mode of payment!"); return cur_frm.call({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account", args: { mode_of_payment: doc.mode_of_payment }, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 585e0a426d..2de2c10c75 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -176,7 +176,8 @@ class SalesInvoice(SellingController): # self.set_customer_defaults() for fieldname in ('territory', 'naming_series', 'currency', 'taxes_and_charges', 'letter_head', 'tc_name', - 'selling_price_list', 'company', 'select_print_heading', 'cash_bank_account'): + 'selling_price_list', 'company', 'select_print_heading', 'cash_bank_account', + 'write_off_account', 'write_off_cost_center'): if (not for_validate) or (for_validate and not self.get(fieldname)): self.set(fieldname, pos.get(fieldname)) diff --git a/erpnext/public/css/erpnext.css b/erpnext/public/css/erpnext.css index fc42be1cee..1c499a586f 100644 --- a/erpnext/public/css/erpnext.css +++ b/erpnext/public/css/erpnext.css @@ -24,14 +24,18 @@ overflow: hidden; text-overflow: ellipsis; cursor: pointer; - padding: 10px; + padding: 5px; height: 0px; - padding-bottom: 35%; + padding-bottom: 38%; width: 30%; margin: 1.6%; border: 1px solid #d1d8dd; } +.pos-item-text { + padding: 0px 5px; +} + .pos-item .item-code { margin-bottom: 0px; } diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html index d26e0b606c..1c337f42c1 100644 --- a/erpnext/public/js/pos/pos.html +++ b/erpnext/public/js/pos/pos.html @@ -2,10 +2,7 @@
-
-
- -
+
@@ -35,10 +32,10 @@
{%= __("Grand Total") %}
-
diff --git a/erpnext/public/js/pos/pos.js b/erpnext/public/js/pos/pos.js index ae4c6a5c2c..69501826ae 100644 --- a/erpnext/public/js/pos/pos.js +++ b/erpnext/public/js/pos/pos.js @@ -1,7 +1,9 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -erpnext.POS = Class.extend({ +frappe.provide("erpnext.pos"); + +erpnext.pos.PointOfSale = Class.extend({ init: function(wrapper, frm) { this.wrapper = wrapper; this.frm = frm; @@ -18,10 +20,6 @@ erpnext.POS = Class.extend({ this.wrapper.find('input.discount-amount').on("change", function() { frappe.model.set_value(me.frm.doctype, me.frm.docname, "discount_amount", this.value); }); - - this.wrapper.find(".make-payment").on("click", function() { - me.make_payment(); - }) }, check_transaction_type: function() { var me = this; @@ -130,7 +128,7 @@ erpnext.POS = Class.extend({ item_price: format_currency(obj.price_list_rate, obj.currency), item_name: obj.name===obj.item_name ? "" : obj.item_name, item_image: obj.image - })).appendTo($wrap); + })).tooltip().appendTo($wrap); }); } @@ -216,12 +214,7 @@ erpnext.POS = Class.extend({ var me = this; this.refresh_item_list(); - this.party_field.set_input(this.frm.doc[this.party.toLowerCase()]); - this.wrapper.find('input.discount-amount').val(this.frm.doc.discount_amount); - - this.show_items_in_item_cart(); - this.show_taxes(); - this.set_totals(); + this.refresh_fields(); // if form is local then only run all these functions if (this.frm.doc.docstatus===0) { @@ -229,13 +222,21 @@ erpnext.POS = Class.extend({ } this.disable_text_box_and_button(); - this.hide_payment_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.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 @@ -285,12 +286,6 @@ erpnext.POS = Class.extend({ me.frm.doc.currency)); this.wrapper.find(".grand-total").text(format_currency(this.frm.doc[this.grand_total], me.frm.doc.currency)); - - $(".paid-amount-area").toggle(!!this.frm.doc.paid_amount); - if(this.frm.doc.paid_amount) { - this.wrapper.find(".paid-amount").text(format_currency(this.frm.doc.paid_amount, - me.frm.doc.currency)); - } }, call_when_local: function() { var me = this; @@ -307,25 +302,10 @@ erpnext.POS = Class.extend({ me.increase_decrease_qty($item, $(this).attr("data-action")); }); - // on td click toggle the highlighting of row - $(this.wrapper).find("#cart tbody tr td").on("click", function() { - var row = $(this).closest("tr"); - if (row.attr("data-selected") == "false") { - row.attr("class", "warning"); - row.attr("data-selected", "true"); - } - else { - row.prop("class", null); - row.attr("data-selected", "false"); - } - me.refresh_delete_btn(); - }); - - me.refresh_delete_btn(); - //me.focus(); + this.focus(); }, focus: function() { - if(me.frm.doc[this.party].toLowerCase()) { + if(this.frm.doc[this.party.toLowerCase()]) { this.search.$input.focus(); } else { if(!(this.frm.doctype == "Quotation" && this.frm.doc.quotation_to!="Customer")) @@ -353,12 +333,28 @@ erpnext.POS = Class.extend({ this.wrapper.find(".pos-item-area").toggleClass("hide", me.frm.doc.docstatus!==0); }, - hide_payment_button: function() { - var toggle = !(this.frm.doctype == "Sales Invoice" && this.frm.doc.is_pos && this.frm.doc.docstatus===1); - $(this.wrapper) - .find(".make-payment") - .toggleClass("hide", toggle) - .prop("disabled", toggle); + set_primary_action: function() { + var me = this; + if (!this.frm.pos_active) 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.clear_actions(); + this.frm.page.set_primary_action(__("Pay"), function() { + me.make_payment(); + }); + this.frm.toolbar.current_status = null; + } else if (this.frm.doc.docstatus===1) { + this.frm.page.clear_actions(); + this.frm.page.set_primary_action(__("New"), function() { + me.frm.pos_active = false; + erpnext.open_as_pos = true; + new_doc(me.frm.doctype); + }); + this.frm.toolbar.current_status = null; + } }, refresh_delete_btn: function() { $(this.wrapper).find(".remove-items").toggle($(".item-cart .warning").length ? true : false); @@ -395,7 +391,6 @@ erpnext.POS = Class.extend({ make_payment: function() { var me = this; var no_of_items = this.frm.doc.items.length; - var mode_of_payment = []; if (no_of_items == 0) msgprint(__("Payment cannot be made for empty cart")); @@ -407,32 +402,81 @@ erpnext.POS = Class.extend({ msgprint(__("Please add to Modes of Payment from Setup.")) return; } - for (x=0; x<=r.message.length - 1; x++) { - mode_of_payment.push(r.message[x].name); - } + + var modes_of_payment = r.message; + + // prefer cash payment! + var default_mode = modes_of_payment.indexOf(__("Cash"))!==-1 ? __("Cash") : undefined; // show payment wizard var dialog = new frappe.ui.Dialog({ width: 400, title: 'Payment', fields: [ - {fieldtype:'Data', fieldname:'total_amount', label:'Total Amount', read_only:1}, - {fieldtype:'Select', fieldname:'mode_of_payment', label:'Mode of Payment', - options:mode_of_payment.join('\n'), reqd: 1}, + {fieldtype:'Currency', fieldname:'total_amount', label: __('Total Amount'), read_only:1, + options:"currency", default:me.frm.doc.grand_total_export, read_only: 1}, + {fieldtype:'Select', fieldname:'mode_of_payment', label: __('Mode of Payment'), + options:modes_of_payment.join('\n'), reqd: 1, default: default_mode}, + {fieldtype:'Currency', fieldname:'paid_amount', label:__('Amount Paid'), reqd:1, + options: "currency", + default:me.frm.doc.grand_total_export, hidden: 1}, + {fieldtype:'Currency', fieldname:'change', label: __('Change'), options: "currency", + default: 0.0, hidden: 1}, + {fieldtype:'Currency', fieldname:'write_off_amount', label: __('Write Off'), options: "currency", + default: 0.0, hidden: 1}, {fieldtype:'Button', fieldname:'pay', label:'Pay'} ] }); - dialog.set_values({ - "total_amount": $(".grand-total").text() - }); dialog.show(); + + // make read only dialog.get_input("total_amount").prop("disabled", true); + dialog.get_input("write_off_amount").prop("disabled", true); + + dialog.get_input("paid_amount").on("change", function() { + var values = dialog.get_values(); + dialog.set_value("change", Math.round(values.paid_amount - values.total_amount)); + dialog.get_input("change").trigger("change"); + }); + + dialog.get_input("change").on("change", function() { + var values = dialog.get_values(); + var write_off_amount = (flt(values.paid_amount) - flt(values.change)) - values.total_amount; + dialog.set_value("write_off_amount", write_off_amount); + dialog.fields_dict.write_off_amount.$wrapper.toggleClass("hide", !!!write_off_amount); + }); + + // 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.fields_dict.paid_amount.$wrapper.toggleClass("hide", !is_cash); + dialog.fields_dict.change.$wrapper.toggleClass("hide", !is_cash); + + if (is_cash && !dialog.get_value("change")) { + // set to nearest 5 + var paid_amount = 5 * Math.ceil(dialog.get_value("total_amount") / 5); + dialog.set_value("paid_amount", paid_amount); + dialog.get_input("paid_amount").trigger("change"); + } + }).trigger("change"); dialog.fields_dict.pay.input.onclick = function() { - me.frm.set_value("mode_of_payment", dialog.get_values().mode_of_payment); - me.frm.set_value("paid_amount", dialog.get_values().total_amount); - me.frm.cscript.mode_of_payment(me.frm.doc); - me.frm.save(); + 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)) / me.frm.doc.conversion_rate, 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(); me.refresh(); }; @@ -441,3 +485,65 @@ erpnext.POS = Class.extend({ } }, }); + +erpnext.pos.make_pos_btn = function(frm) { + // 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.doc.docstatus <= 1) { + if(!frm.pos_active) { + var btn_label = __("POS View"), + icon = "icon-th"; + } else { + var btn_label = __("Form View"), + icon = "icon-file-text"; + } + + if(erpnext.open_as_pos) { + erpnext.pos.toggle(frm, true); + erpnext.open_as_pos = false; + } + + frm.$pos_btn && frm.$pos_btn.remove(); + + frm.$pos_btn = frm.page.add_menu_item(btn_label, function() { + erpnext.pos.toggle(frm); + }); + } else { + // hack: will avoid calling refresh from refresh + setTimeout(function() { erpnext.pos.toggle(frm, false); }, 100); + } +} + +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===true && frm.pos_active) || (show===false && !frm.pos_active)) { + return; + } + + if(show && !price_list) { + frappe.throw(__("Please select Price List")); + } + + // 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.pos_active ? "main" : "pos"); + frm.pos_active = !frm.pos_active; + + frm.refresh(); + + // refresh + if(frm.pos_active) { + frm.pos.refresh(); + } +} diff --git a/erpnext/public/js/pos/pos_item.html b/erpnext/public/js/pos/pos_item.html index 246eb4eb9c..481f041807 100644 --- a/erpnext/public/js/pos/pos_item.html +++ b/erpnext/public/js/pos/pos_item.html @@ -1,10 +1,9 @@ -
+
-
{%= item_code %}
-
- {% if (item_name) { %}{%= item_name %}
{% } %} - {%= item_price %} +
+
{%= item_code %}
+
{%= item_price %}
diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js index 3b6beed889..107114ec64 100644 --- a/erpnext/public/js/transaction.js +++ b/erpnext/public/js/transaction.js @@ -58,68 +58,10 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.show_item_wise_taxes(); this.set_dynamic_labels(); - // Show POS button only if it is enabled from features setup - if(cint(sys_defaults.fs_pos_view)===1 && this.frm.doctype!="Material Request") { - this.make_pos_btn(); - } + erpnext.pos.make_pos_btn(this.frm); + }, - make_pos_btn: function() { - var me = this; - if(this.frm.doc.docstatus <= 1) { - if(!this.pos_active) { - var btn_label = __("POS View"), - icon = "icon-th"; - } else { - var btn_label = __("Form View"), - icon = "icon-file-text"; - } - - if(erpnext.open_as_pos) { - me.toggle_pos(true); - erpnext.open_as_pos = false; - } - - this.$pos_btn && this.$pos_btn.remove(); - - this.$pos_btn = this.frm.page.add_menu_item(btn_label, function() { - me.toggle_pos(); - }); - } else { - // hack: will avoid calling refresh from refresh - setTimeout(function() { me.toggle_pos(false); }, 100); - } - }, - - toggle_pos: function(show) { - // Check whether it is Selling or Buying cycle - var price_list = frappe.meta.has_field(cur_frm.doc.doctype, "selling_price_list") ? - this.frm.doc.selling_price_list : this.frm.doc.buying_price_list; - - if((show===true && this.pos_active) || (show===false && !this.pos_active)) - return; - - if(show && !price_list) { - frappe.throw(__("Please select Price List")); - } - - // make pos - if(!this.frm.pos) { - var wrapper = this.frm.page.add_view("pos", "
"); - this.frm.pos = new erpnext.POS(wrapper, this.frm); - } - - // toggle view - this.frm.page.set_view(this.pos_active ? "main" : "pos"); - this.pos_active = !this.pos_active; - - // refresh - if(this.pos_active) - this.frm.pos.refresh(); - this.frm.refresh(); - }, - - item_code: function(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 490eec0dbd..4d76158b63 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -408,9 +408,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ if(this.frm.doc.is_pos) { if(!this.frm.doc.paid_amount || update_paid_amount===undefined || update_paid_amount) { this.frm.doc.paid_amount = flt(total_amount_to_pay); + this.frm.refresh_field("paid_amount"); } } else { this.frm.doc.paid_amount = 0 + this.frm.refresh_field("paid_amount"); } this.frm.set_value("outstanding_amount", flt(total_amount_to_pay