diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index 2428a7dae91..fb5569a5f0c 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -8,6 +8,7 @@ cur_frm.cscript.other_fname = "purchase_tax_details"; wn.provide("erpnext.accounts"); wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); wn.require('app/buying/doctype/purchase_common/purchase_common.js'); +wn.require('app/accounts/doctype/sales_invoice/pos.js'); erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ onload: function() { diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index 9a6b9010235..46faf3df599 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -61,19 +61,23 @@ class DocType(BuyingController): "purchase_receipt_details") def get_credit_to(self): - acc_head = webnotes.conn.sql("""select name, credit_days from `tabAccount` - where (name = %s or (master_name = %s and master_type = 'supplier')) - and docstatus != 2 and company = %s""", - (cstr(self.doc.supplier) + " - " + self.company_abbr, - self.doc.supplier, self.doc.company)) - ret = {} - if acc_head and acc_head[0][0]: - ret['credit_to'] = acc_head[0][0] - if not self.doc.due_date: - ret['due_date'] = add_days(cstr(self.doc.posting_date), acc_head and cint(acc_head[0][1]) or 0) - elif not acc_head: - msgprint("%s does not have an Account Head in %s. You must first create it from the Supplier Master" % (self.doc.supplier, self.doc.company)) + if self.doc.supplier: + acc_head = webnotes.conn.sql("""select name, credit_days from `tabAccount` + where (name = %s or (master_name = %s and master_type = 'supplier')) + and docstatus != 2 and company = %s""", + (cstr(self.doc.supplier) + " - " + self.company_abbr, + self.doc.supplier, self.doc.company)) + + if acc_head and acc_head[0][0]: + ret['credit_to'] = acc_head[0][0] + if not self.doc.due_date: + ret['due_date'] = add_days(cstr(self.doc.posting_date), + acc_head and cint(acc_head[0][1]) or 0) + elif not acc_head: + msgprint("%s does not have an Account Head in %s. \ + You must first create it from the Supplier Master" % \ + (self.doc.supplier, self.doc.company)) return ret def set_supplier_defaults(self): diff --git a/accounts/doctype/sales_invoice/pos.js b/accounts/doctype/sales_invoice/pos.js index 8837aed14b0..c68b9915c88 100644 --- a/accounts/doctype/sales_invoice/pos.js +++ b/accounts/doctype/sales_invoice/pos.js @@ -7,7 +7,7 @@ erpnext.POS = Class.extend({ this.frm = frm; this.wrapper.html('
\
\ -
\ +
\
\
\
\ @@ -71,7 +71,18 @@ erpnext.POS = Class.extend({
\
\ '); - + + if (wn.meta.has_field(cur_frm.doc.doctype, "customer")) { + this.party = "Customer"; + this.price_list = this.frm.doc.selling_price_list; + this.sales_or_purchase = "Sales"; + } + else if (wn.meta.has_field(cur_frm.doc.doctype, "supplier")) { + this.party = "Supplier"; + this.price_list = this.frm.doc.buying_price_list; + this.sales_or_purchase = "Purchase"; + } + this.make(); var me = this; @@ -88,28 +99,29 @@ erpnext.POS = Class.extend({ }); }, make: function() { - this.make_customer(); + this.make_party(); this.make_item_group(); this.make_search(); this.make_barcode(); this.make_item_list(); }, - make_customer: function() { + make_party: function() { var me = this; - this.customer = wn.ui.form.make_control({ + this.party_field = wn.ui.form.make_control({ df: { "fieldtype": "Link", - "options": "Customer", - "label": "Customer", - "fieldname": "pos_customer", - "placeholder": "Customer" + "options": this.party, + "label": this.party, + "fieldname": "pos_party", + "placeholder": this.party }, - parent: this.wrapper.find(".customer-area") + parent: this.wrapper.find(".party-area") }); - this.customer.make_input(); - this.customer.$input.on("change", function() { - if(!me.customer.autocomplete_open) - wn.model.set_value("Sales Invoice", me.frm.docname, "customer", this.value); + this.party_field.make_input(); + this.party_field.$input.on("change", function() { + if(!me.party_field.autocomplete_open) + wn.model.set_value(me.frm.doctype, me.frm.docname, + me.party.toLowerCase(), this.value); }); }, make_item_group: function() { @@ -120,7 +132,7 @@ erpnext.POS = Class.extend({ "options": "Item Group", "label": "Item Group", "fieldname": "pos_item_group", - "placeholder": "Filter by Item Group" + "placeholder": "Item Group" }, parent: this.wrapper.find(".item-group-area") }); @@ -138,7 +150,7 @@ erpnext.POS = Class.extend({ "options": "Item", "label": "Item", "fieldname": "pos_item", - "placeholder": "Select Item" + "placeholder": "Item" }, parent: this.wrapper.find(".search-area") }); @@ -155,7 +167,7 @@ erpnext.POS = Class.extend({ "fieldtype": "Data", "label": "Barcode", "fieldname": "pos_barcode", - "placeholder": "Select Barcode" + "placeholder": "Barcode" }, parent: this.wrapper.find(".barcode-area") }); @@ -171,7 +183,8 @@ erpnext.POS = Class.extend({ wn.call({ method: 'accounts.doctype.sales_invoice.pos.get_items', args: { - price_list: cur_frm.doc.selling_price_list, + sales_or_purchase: this.sales_or_purchase, + price_list: this.price_list, item_group: this.item_group.$input.val(), item: this.search.$input.val() }, @@ -200,15 +213,18 @@ erpnext.POS = Class.extend({ }); // if form is local then allow this function - if (cur_frm.doc.docstatus===0) { - $("div.pos-item").on("click", function() { - if(!cur_frm.doc.customer) { - msgprint("Please select customer first."); + $(me.wrapper).find("div.pos-item").on("click", function() { + if(me.frm.doc.docstatus==0) { + if(!me.frm.doc[me.party.toLowerCase()] && ((me.frm.doctype == "Quotation" && + me.frm.doc.quotation_to == "Customer") + || me.frm.doctype != "Quotation")) { + msgprint("Please select " + me.party + " first."); return; } - me.add_to_cart($(this).attr("data-item_code")); - }); - } + else + me.add_to_cart($(this).attr("data-item_code")); + } + }); } }); }, @@ -217,12 +233,12 @@ erpnext.POS = Class.extend({ var caught = false; // get no_of_items - no_of_items = me.wrapper.find("#cart tbody").length; - + var no_of_items = me.wrapper.find("#cart tbody tr").length; + // check whether the item is already added if (no_of_items != 0) { - $.each(wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries", - "Sales Invoice"), function(i, d) { + $.each(wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name, + this.frm.cscript.fname, this.frm.doctype), function(i, d) { if (d.item_code == item_code) caught = true; }); @@ -233,15 +249,16 @@ erpnext.POS = Class.extend({ me.update_qty(item_code, 1); } else { - var child = wn.model.add_child(me.frm.doc, "Sales Invoice Item", "entries"); + var child = wn.model.add_child(me.frm.doc, this.frm.doctype + " Item", + this.frm.cscript.fname); child.item_code = item_code; me.frm.cscript.item_code(me.frm.doc, child.doctype, child.name); } }, update_qty: function(item_code, qty, textbox_qty) { var me = this; - $.each(wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries", - "Sales Invoice"), function(i, d) { + $.each(wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name, + this.frm.cscript.fname, this.frm.doctype), function(i, d) { if (d.item_code == item_code) { if (textbox_qty) { if (qty == 0 && d.item_code == item_code) @@ -259,14 +276,24 @@ erpnext.POS = Class.extend({ }, refresh: function() { var me = this; - this.customer.set_input(this.frm.doc.customer); + this.party_field.set_input(this.frm.doc[this.party.toLowerCase()]); this.barcode.set_input(""); // add items var $items = me.wrapper.find("#cart tbody").empty(); - $.each(wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries", - "Sales Invoice"), function(i, d) { + $.each(wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name, + this.frm.cscript.fname, this.frm.doctype), function(i, d) { + + if (me.sales_or_purchase == "Sales") { + item_amount = d.export_amount; + rate = d.export_rate; + } + else { + item_amount = d.import_amount; + rate = d.import_rate; + } + $(repl('\ %(item_code)s%(item_name)s\ " + d.item_name), qty: d.qty, - rate: format_currency(d.ref_rate, cur_frm.doc.price_list_currency), - amount: format_currency(d.export_amount, cur_frm.doc.price_list_currency) + rate: format_currency(rate, me.frm.doc.currency), + amount: format_currency(item_amount, me.frm.doc.currency) } )).appendTo($items); }); // taxes - var taxes = wn.model.get_children("Sales Taxes and Charges", this.frm.doc.name, "other_charges", - "Sales Invoice"); - $(".tax-table") + var taxes = wn.model.get_children(this.sales_or_purchase + " Taxes and Charges", + this.frm.doc.name, this.frm.cscript.other_fname, this.frm.doctype); + $(this.wrapper).find(".tax-table") .toggle((taxes && taxes.length) ? true : false) .find("tbody").empty(); @@ -297,30 +324,39 @@ erpnext.POS = Class.extend({ ', { description: d.description, rate: d.rate, - tax_amount: format_currency(d.tax_amount, me.frm.doc.price_list_currency) + tax_amount: format_currency(flt(d.tax_amount)/flt(me.frm.doc.conversion_rate), + me.frm.doc.currency) })).appendTo(".tax-table tbody"); }); // set totals - this.wrapper.find(".net-total").text(format_currency(this.frm.doc.net_total_export, - cur_frm.doc.price_list_currency)); - this.wrapper.find(".grand-total").text(format_currency(this.frm.doc.grand_total_export, - cur_frm.doc.price_list_currency)); + if (this.sales_or_purchase == "Sales") { + this.wrapper.find(".net-total").text(format_currency(this.frm.doc.net_total_export, + me.frm.doc.currency)); + this.wrapper.find(".grand-total").text(format_currency(this.frm.doc.grand_total_export, + me.frm.doc.currency)); + } + else { + this.wrapper.find(".net-total").text(format_currency(this.frm.doc.net_total_import, + me.frm.doc.currency)); + this.wrapper.find(".grand-total").text(format_currency(this.frm.doc.grand_total_import, + me.frm.doc.currency)); + } // if form is local then only run all these functions - if (cur_frm.doc.docstatus===0) { - $("input.qty").on("focus", function() { + if (this.frm.doc.docstatus===0) { + $(this.wrapper).find("input.qty").on("focus", function() { $(this).select(); }); // append quantity to the respective item after change from input box - $("input.qty").on("change", function() { + $(this.wrapper).find("input.qty").on("change", function() { var item_code = $(this).closest("tr")[0].id; me.update_qty(item_code, $(this).val(), true); }); // on td click toggle the highlighting of row - $("#cart tbody tr td").on("click", function() { + $(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"); @@ -335,20 +371,37 @@ erpnext.POS = Class.extend({ }); me.refresh_delete_btn(); - cur_frm.pos.barcode.$input.focus(); + this.barcode.$input.focus(); } // if form is submitted & cancelled then disable all input box & buttons - if (cur_frm.doc.docstatus>=1 && cint(cur_frm.doc.is_pos)) { - me.wrapper.find('input, button').each(function () { + if (this.frm.doc.docstatus>=1) { + $(this.wrapper).find('input, button').each(function () { $(this).prop('disabled', true); }); - $(".delete-items").hide(); - $(".make-payment").hide(); + $(this.wrapper).find(".delete-items").hide(); + $(this.wrapper).find(".make-payment").hide(); + } + else { + $(this.wrapper).find('input, button').each(function () { + $(this).prop('disabled', false); + }); + $(this.wrapper).find(".make-payment").show(); + } + + // Show Make Payment button only in Sales Invoice + if (this.frm.doctype != "Sales Invoice") + $(this.wrapper).find(".make-payment").hide(); + + // If quotation to is not Customer then remove party + if (this.frm.doctype == "Quotation") { + this.party_field.$wrapper.remove(); + if (this.frm.doc.quotation_to == "Customer") + this.make_party(); } }, refresh_delete_btn: function() { - $(".delete-items").toggle($(".item-cart .warning").length ? true : false); + $(this.wrapper).find(".delete-items").toggle($(".item-cart .warning").length ? true : false); }, add_item_thru_barcode: function() { var me = this; @@ -370,32 +423,32 @@ erpnext.POS = Class.extend({ remove_selected_item: function() { var me = this; var selected_items = []; - var no_of_items = $("#cart tbody tr").length; + var no_of_items = $(this.wrapper).find("#cart tbody tr").length; for(var x=0; x<=no_of_items - 1; x++) { - var row = $("#cart tbody tr:eq(" + 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 = wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries", - "Sales Invoice"); + + var child = wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name, + this.frm.cscript.fname, this.frm.doctype); + $.each(child, function(i, d) { for (var i in selected_items) { if (d.item_code == selected_items[i]) { - // cur_frm.fields_dict["entries"].grid.grid_rows[d.idx].remove(); wn.model.clear_doc(d.doctype, d.name); } } }); - cur_frm.fields_dict["entries"].grid.refresh(); - cur_frm.script_manager.trigger("calculate_taxes_and_totals"); + this.frm.fields_dict[this.frm.cscript.fname].grid.refresh(); + this.frm.script_manager.trigger("calculate_taxes_and_totals"); me.frm.dirty(); me.refresh(); }, make_payment: function() { var me = this; - var no_of_items = $("#cart tbody tr").length; + var no_of_items = $(this.wrapper).find("#cart tbody tr").length; var mode_of_payment = []; if (no_of_items == 0) @@ -423,15 +476,15 @@ erpnext.POS = Class.extend({ "total_amount": $(".grand-total").text() }); dialog.show(); - cur_frm.pos.barcode.$input.focus(); + me.barcode.$input.focus(); dialog.get_input("total_amount").prop("disabled", true); dialog.fields_dict.pay.input.onclick = function() { - cur_frm.set_value("mode_of_payment", dialog.get_values().mode_of_payment); - cur_frm.set_value("paid_amount", dialog.get_values().total_amount); - cur_frm.cscript.mode_of_payment(cur_frm.doc); - cur_frm.save(); + 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(); dialog.hide(); me.refresh(); }; diff --git a/accounts/doctype/sales_invoice/pos.py b/accounts/doctype/sales_invoice/pos.py index 08340f76c5e..44fe40d8de9 100644 --- a/accounts/doctype/sales_invoice/pos.py +++ b/accounts/doctype/sales_invoice/pos.py @@ -3,17 +3,21 @@ from __future__ import unicode_literals import webnotes -from webnotes import msgprint @webnotes.whitelist() -def get_items(price_list, item=None, item_group=None): +def get_items(price_list, sales_or_purchase, item=None, item_group=None): condition = "" + if sales_or_purchase == "Sales": + condition = "i.is_sales_item='Yes'" + else: + condition = "i.is_purchase_item='Yes'" + if item_group and item_group != "All Item Groups": - condition = "and i.item_group='%s'" % item_group + condition += " and i.item_group='%s'" % item_group if item: - condition = "and i.name='%s'" % item + condition += " and i.name='%s'" % item return webnotes.conn.sql("""select i.name, i.item_name, i.image, pl_items.ref_rate, pl_items.currency @@ -24,13 +28,18 @@ def get_items(price_list, item=None, item_group=None): ON pl_items.item_code=i.name where - i.is_sales_item='Yes'%s""" % ('%s', condition), (price_list), as_dict=1) + %s""" % ('%s', condition), (price_list), as_dict=1) @webnotes.whitelist() def get_item_from_barcode(barcode): return webnotes.conn.sql("""select name from `tabItem` where barcode=%s""", (barcode), as_dict=1) +@webnotes.whitelist() +def get_item_from_serial_no(serial_no): + return webnotes.conn.sql("""select name, item_code from `tabSerial No` where + name=%s""", (serial_no), as_dict=1) + @webnotes.whitelist() def get_mode_of_payment(): return webnotes.conn.sql("""select name from `tabMode of Payment`""", as_dict=1) \ No newline at end of file diff --git a/accounts/doctype/sales_invoice/sales_invoice.css b/accounts/doctype/sales_invoice/sales_invoice.css deleted file mode 100644 index e4b61b66ca8..00000000000 --- a/accounts/doctype/sales_invoice/sales_invoice.css +++ /dev/null @@ -1,15 +0,0 @@ -.pos-item { - height: 200px; - overflow: hidden; - cursor: pointer; - padding-left: 5px !important; - padding-right: 5px !important; -} - -.pos-bill { - padding: 20px 5px; - font-family: Monospace; - border: 1px solid #eee; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); -} \ No newline at end of file diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index aeae28ce03e..5220c0fa20c 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -29,9 +29,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte // toggle to pos view if is_pos is 1 in user_defaults if ((cint(wn.defaults.get_user_defaults("is_pos"))===1 || cur_frm.doc.is_pos) && cint(wn.defaults.get_user_defaults("fs_pos_view"))===1) { - this.frm.set_value("is_pos", 1); - this.is_pos(); - cur_frm.cscript.toggle_pos(true); + if(this.frm.doc.__islocal && !this.frm.doc.amended_from) { + this.frm.set_value("is_pos", 1); + this.is_pos(function() {cur_frm.cscript.toggle_pos(true);}); + } } // if document is POS then change default print format to "POS Invoice" @@ -78,14 +79,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); } - if (doc.docstatus===0) { + // Show buttons only when pos view is active + if (doc.docstatus===0 && !this.pos_active) { cur_frm.cscript.sales_order_btn(); cur_frm.cscript.delivery_note_btn(); } - - // Show POS button only if it enabled from features setup - if(cint(sys_defaults.fs_pos_view)===1) - cur_frm.cscript.pos_btn(); }, sales_order_btn: function() { @@ -124,62 +122,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte }); }); }, - - pos_btn: function() { - if(cur_frm.$pos_btn) - cur_frm.$pos_btn.remove(); - - if(!cur_frm.pos_active) { - var btn_label = wn._("POS View"), - icon = "icon-desktop"; - - cur_frm.cscript.sales_order_btn(); - cur_frm.cscript.delivery_note_btn(); - } else { - var btn_label = wn._("Invoice View"), - icon = "icon-file-text"; - - if (cur_frm.doc.docstatus===0) { - this.$delivery_note_btn.remove(); - this.$sales_order_btn.remove(); - } - } - - cur_frm.$pos_btn = cur_frm.add_custom_button(btn_label, function() { - cur_frm.cscript.toggle_pos(); - cur_frm.cscript.pos_btn(); - }, icon); - }, - - toggle_pos: function(show) { - if (!this.frm.doc.selling_price_list) - msgprint(wn._("Please select Price List")) - else { - if((show===true && cur_frm.pos_active) || (show===false && !cur_frm.pos_active)) return; - - // make pos - if(!cur_frm.pos) { - cur_frm.layout.add_view("pos"); - cur_frm.pos = new erpnext.POS(cur_frm.layout.views.pos, cur_frm); - } - - // toggle view - cur_frm.layout.set_view(cur_frm.pos_active ? "" : "pos"); - cur_frm.pos_active = !cur_frm.pos_active; - - // refresh - if(cur_frm.pos_active) - cur_frm.pos.refresh(); - } - }, tc_name: function() { this.get_terms(); }, - is_pos: function() { + is_pos: function(callback_fn) { cur_frm.cscript.hide_fields(this.frm.doc); - if(cint(this.frm.doc.is_pos)) { if(!this.frm.doc.company) { this.frm.set_value("is_pos", 0); @@ -192,11 +141,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte callback: function(r) { if(!r.exc) { me.frm.script_manager.trigger("update_stock"); + if(callback_fn) callback_fn() } } }); } } + }, debit_to: function() { diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 12deed73a87..2eb9ae84cb0 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -195,7 +195,7 @@ class DocType(SellingController): pos = get_pos_settings(self.doc.company) if pos: - if not for_validate: + if not for_validate and not self.doc.customer: self.doc.customer = pos.customer self.set_customer_defaults() diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index 3c9075d9a59..77cf2ccef2b 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-24 19:29:05", "docstatus": 0, - "modified": "2013-10-02 14:24:52", + "modified": "2013-10-02 14:24:50", "modified_by": "Administrator", "owner": "Administrator" }, @@ -181,6 +181,7 @@ "search_index": 1 }, { + "default": "Today", "description": "Enter the date by which payments from customer is expected against this invoice.", "doctype": "DocField", "fieldname": "due_date", @@ -412,7 +413,7 @@ "doctype": "DocField", "fieldname": "other_charges", "fieldtype": "Table", - "label": "Taxes and Charges1", + "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges", diff --git a/accounts/report/gross_profit/gross_profit.py b/accounts/report/gross_profit/gross_profit.py index d9c20d5ccc7..9917b69697f 100644 --- a/accounts/report/gross_profit/gross_profit.py +++ b/accounts/report/gross_profit/gross_profit.py @@ -55,7 +55,7 @@ def get_stock_ledger_entries(filters): from `tabStock Ledger Entry`""" if filters.get("company"): - query += """ and company=%(company)s""" + query += """ where company=%(company)s""" query += " order by item_code desc, warehouse desc, posting_date desc, posting_time desc, name desc" diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index 2dfe65515a8..433a76fba7c 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -108,8 +108,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ var item = wn.model.get_doc(cdt, cdn); if(item.item_code) { if(!this.validate_company_and_party("supplier")) { - item.item_code = null; - refresh_field("item_code", item.name, item.parentfield); + cur_frm.fields_dict[me.frm.cscript.fname].grid.grid_rows[item.idx - 1].remove(); } else { return this.frm.call({ method: "buying.utils.get_item_details", diff --git a/buying/doctype/purchase_order/purchase_order.js b/buying/doctype/purchase_order/purchase_order.js index a960046b75c..9967f15a50b 100644 --- a/buying/doctype/purchase_order/purchase_order.js +++ b/buying/doctype/purchase_order/purchase_order.js @@ -10,6 +10,7 @@ cur_frm.cscript.other_fname = "purchase_tax_details"; wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); wn.require('app/buying/doctype/purchase_common/purchase_common.js'); +wn.require('app/accounts/doctype/sales_invoice/pos.js'); erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({ refresh: function(doc, cdt, cdn) { diff --git a/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py index e160babc59a..cef5e4a207b 100644 --- a/buying/doctype/purchase_order/test_purchase_order.py +++ b/buying/doctype/purchase_order/test_purchase_order.py @@ -6,9 +6,10 @@ from __future__ import unicode_literals import unittest import webnotes import webnotes.defaults +from webnotes.utils import flt class TestPurchaseOrder(unittest.TestCase): - def test_make_purchase_receipt(self): + def test_make_purchase_receipt(self): from buying.doctype.purchase_order.purchase_order import make_purchase_receipt po = webnotes.bean(copy=test_records[0]).insert() @@ -18,6 +19,7 @@ class TestPurchaseOrder(unittest.TestCase): po = webnotes.bean("Purchase Order", po.doc.name) po.submit() + pr = make_purchase_receipt(po.doc.name) pr[0]["supplier_warehouse"] = "_Test Warehouse 1 - _TC" @@ -25,7 +27,52 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(pr), len(test_records[0])) pr[0].naming_series = "_T-Purchase Receipt-" - webnotes.bean(pr).insert() + pr_bean = webnotes.bean(pr) + pr_bean.insert() + + def test_ordered_qty(self): + webnotes.conn.sql("delete from tabBin") + + from buying.doctype.purchase_order.purchase_order import make_purchase_receipt + + po = webnotes.bean(copy=test_records[0]).insert() + + self.assertRaises(webnotes.ValidationError, make_purchase_receipt, + po.doc.name) + + po = webnotes.bean("Purchase Order", po.doc.name) + po.doc.is_subcontracted = "No" + po.doclist[1].item_code = "_Test Item" + po.submit() + + self.assertEquals(webnotes.conn.get_value("Bin", {"item_code": "_Test Item", + "warehouse": "_Test Warehouse - _TC"}, "ordered_qty"), 10) + + pr = make_purchase_receipt(po.doc.name) + + self.assertEquals(pr[0]["doctype"], "Purchase Receipt") + self.assertEquals(len(pr), len(test_records[0])) + + pr[0].naming_series = "_T-Purchase Receipt-" + pr[1].qty = 4.0 + pr_bean = webnotes.bean(pr) + pr_bean.insert() + pr_bean.submit() + + self.assertEquals(flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item", + "warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 6.0) + + webnotes.conn.set_value('Item', '_Test Item', 'tolerance', 50) + + pr1 = make_purchase_receipt(po.doc.name) + pr1[0].naming_series = "_T-Purchase Receipt-" + pr1[1].qty = 8 + pr1_bean = webnotes.bean(pr1) + pr1_bean.insert() + pr1_bean.submit() + + self.assertEquals(flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item", + "warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 0.0) def test_make_purchase_invocie(self): from buying.doctype.purchase_order.purchase_order import make_purchase_invoice diff --git a/buying/doctype/supplier_quotation/supplier_quotation.js b/buying/doctype/supplier_quotation/supplier_quotation.js index cd6127d2355..3fba931f995 100644 --- a/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/buying/doctype/supplier_quotation/supplier_quotation.js @@ -9,6 +9,7 @@ cur_frm.cscript.other_fname = "purchase_tax_details"; // attach required files wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); wn.require('app/buying/doctype/purchase_common/purchase_common.js'); +wn.require('app/accounts/doctype/sales_invoice/pos.js'); erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({ refresh: function() { diff --git a/buying/utils.py b/buying/utils.py index ce81b6bd67b..115b023962e 100644 --- a/buying/utils.py +++ b/buying/utils.py @@ -65,7 +65,7 @@ def _get_basic_details(args, item_bean): out = webnotes._dict({ "description": item.description_html or item.description, - "qty": 0.0, + "qty": 1.0, "uom": item.stock_uom, "conversion_factor": 1.0, "warehouse": args.warehouse or item.default_warehouse, diff --git a/docs/user/accounts/docs.user.accounts.opening_stock.md b/docs/user/accounts/docs.user.accounts.opening_stock.md index 3ba5025747a..b0927bd8652 100644 --- a/docs/user/accounts/docs.user.accounts.opening_stock.md +++ b/docs/user/accounts/docs.user.accounts.opening_stock.md @@ -17,5 +17,28 @@ To make a Stock Reconciliation, go to: and follow the steps mentioned on the page. -![Stock Reconciliation](img/stock-reconciliation1.png) +#### Step 1: Download Template + +![Stock Reconciliation](img/stock-reconciliation-1.png) + +#### Step 2: Enter Data in csv file. + + +![Stock Reconciliation](img/stock-reconciliation-csv-1.png) + +The csv format is case-sensitive. Thus special care should be taken to avoid spelling errors or wrong names. Even if you do not list some quantities or valuation rates, the file will still process the data. + +#### Step 3: Upload the csv file with data + +![Stock Reconciliation](img/stock-reconciliation-2.png) + +
+ +#### Step 4: Attach the uploaded file. + +![Stock Reconciliation](img/stock-reconciliation-3.png) + + + +After reviewing saved Reconciliation Data, submit the Stock Reconciliation. On successful submission, the data will be updated in the system. To check the uploaded data go to Stock and view Stock Level Report. \ No newline at end of file diff --git a/docs/user/buying/docs.user.buying.supplier.md b/docs/user/buying/docs.user.buying.supplier.md index e477fa2beda..3707fbe8523 100644 --- a/docs/user/buying/docs.user.buying.supplier.md +++ b/docs/user/buying/docs.user.buying.supplier.md @@ -20,7 +20,15 @@ You can create a new Supplier via: > Tip: When you select a Supplier in any transaction, one Contact and Address gets pre-selected. This is the “Default Contact or Address”. So make sure you set your defaults correctly! +### Integration with Accounts +In ERPNext, there is a separate Account record for each Supplier, of Each company. + +When you create a new Supplier, ERPNext will automatically create an Account Ledger for the Supplier under “Accounts Receivable” in the Company set in the Supplier record. + +> Advanced Tip: If you want to change the Account Group under which the Supplier Account is created, you can set it in the Company master. + +If you want to create an Account in another Company, just change the Company value and “Save” the Supplier again. > Buying > Contact > New Contact diff --git a/docs/user/buying/docs.user.buying.supplier_type.md b/docs/user/buying/docs.user.buying.supplier_type.md index deaf01fcfcd..3871991d638 100644 --- a/docs/user/buying/docs.user.buying.supplier_type.md +++ b/docs/user/buying/docs.user.buying.supplier_type.md @@ -3,9 +3,11 @@ "_label": "Supplier Type" } --- +A supplier may be distinguished from a contractor or subcontractor, who commonly adds specialized input to deliverables. A supplier is also known as a vendor. There are different types of suppliers based on their goods and products. -Based on what the suppliers supply, they are classified into different categories called Supplier Type. -There can be different types of suppliers. You can create your own category of Supplier Type. +ERPNext allows you to create your own categories of suppliers. These categories are known as Supplier Type. For Example, if your suppliers are mainly pharmaceutical companies and FMCG distributors, You can create a new Type for them and name them accordingly. + +Based on what the suppliers supply, they are classified into different categories called Supplier Type. There can be different types of suppliers. You can create your own category of Supplier Type. > Buying > Supplier Type > New Supplier Type diff --git a/docs/user/docs.user.md b/docs/user/docs.user.md index d15a573e1a2..c45ca30a221 100644 --- a/docs/user/docs.user.md +++ b/docs/user/docs.user.md @@ -85,6 +85,7 @@ Contents 1. [Purchase Receipt](docs.user.stock.purchase_receipt.html) 1. [Delivery Note](docs.user.stock.delivery_note.html) 1. [Stock Entry / Material Transfer](docs.user.stock.stock_entry.html) + 1. [Opening Stock](docs.user.accounts.opening_stock.html) 1. [Material Issue](docs.user.stock.material_issue.html) 1. [Sales Return](docs.user.stock.sales_return.html) 1. [Purchase Return](docs.user.stock.purchase_return.html) @@ -100,7 +101,6 @@ Contents 1. [Payment Entry](docs.user.accounts.payments.html) 1. [Journal Voucher](docs.user.accounts.journal_voucher.html) 1. [Opening Entry](docs.user.accounts.opening_entry.html) - 1. [Opening Stock](docs.user.accounts.opening_stock.html) 1. [Period Closing](docs.user.accounts.closing.html) 1. [Accounting Reports](docs.user.accounts.reports.html) 1. [Point of Sale (POS) Invoice](docs.user.accounts.pos.html) @@ -151,3 +151,7 @@ Contents 1. [Fiscal Year](docs.user.knowledge.fiscal_year.html) 1. [Accounting Knowledge](docs.user.knowledge.accounting.html) 1. [Accounting Entries](docs.user.knowledge.accounting_entries.html) + 1. [DocType Definitions](docs.user.knowledge.doctype.html) + 1. [Attachment and CSV Files](docs.user.knowledge.attachment_csv.html) + 1. [Format using Markdown](docs.user.knowledge.markdown.html) + diff --git a/docs/user/knowledge/docs.user.knowledge.accounting.md b/docs/user/knowledge/docs.user.knowledge.accounting.md index 17304984cb0..995d38cfe97 100644 --- a/docs/user/knowledge/docs.user.knowledge.accounting.md +++ b/docs/user/knowledge/docs.user.knowledge.accounting.md @@ -22,34 +22,34 @@ Accounting Entries The balance of account can be increased / decreased, depending on account type and transaction type. - +
- - - + + + - - - + + + - - - + + + - - - + + + - - - + + +
Account TypeTransaction TypeEffect on account balanceAccount TypeTransaction TypeEffect on account balance
DebitDebitIncreasesDebitDebitIncreases
DebitCreditDecreasesDebitCreditDecreases
CreditCreditIncreasesCreditCreditIncreases
CreditDebitDecreasesCreditDebitDecreases
@@ -62,48 +62,48 @@ This means that every accounting entry has two parts, one debit and one credit a As the company will receive a payment from customer, the customer is considered as an asset account. For booking income, company maintains an account called "Sales of Laptop". So, entries will be done in the following manner: - +
- - - + + + - - - + + + - - - + + +
AccountDebitCreditAccountDebitCredit
Customer A50000Customer A50000
Sales of Laptop50000Sales of Laptop50000
Customer A has made the payment, so customer balance should decreased based on the paid amount, which will increase "Cash" balance. - +
- - - + + + - - - + + + - - - + + +
AccountDebitCreditAccountDebitCredit
Customer A50000Customer A50000
Cash50000Cash50000
diff --git a/docs/user/knowledge/docs.user.knowledge.attachment_csv.md b/docs/user/knowledge/docs.user.knowledge.attachment_csv.md new file mode 100644 index 00000000000..311934d7f47 --- /dev/null +++ b/docs/user/knowledge/docs.user.knowledge.attachment_csv.md @@ -0,0 +1,16 @@ +--- +{ + "_label": "Attachement and CSV files" +} +--- + +#### How to Attach files? + +When you open a form, on the right sidebar, you will see a section to attach files. Click on “Add” and select the file you want to attach. Click on “Upload” and you are set. + +#### What is a CSV file? + +A CSV (Comma Separated Value) file is a data file that you can upload into ERPNext to update various data. Any spreadsheet file from popular spreadsheet applications like MS Excel or Open Office Spreadsheet can be saved as a CSV file. + +If you are using Microsoft Excel and using non-English characters, make sure to save your file encoded as UTF-8. For older versions of Excel, there is no clear way of saving as UTF-8. So save your file as a CSV, then open it in Notepad, and save as “UTF-8”. (Sorry blame Microsoft for this!) + diff --git a/docs/user/knowledge/docs.user.knowledge.doctype.md b/docs/user/knowledge/docs.user.knowledge.doctype.md new file mode 100644 index 00000000000..a2550d4f95f --- /dev/null +++ b/docs/user/knowledge/docs.user.knowledge.doctype.md @@ -0,0 +1,267 @@ +--- +{ + "_label": "DocType" +} +--- + + + +ERPNext is a based on a “metadata” (data about data) framework that helps define all the different types of documents in the system. The basic building block of ERPNext is a DocType. + +A DocType represents both a table in the database and a form from which a user can enter data. + +Many DocTypes are single tables, but some work in groups. For example, Quotation has a “Quotation” DocType and a “Quotation Item” doctype for the Items table, among others. DocTypes contain a collection of fields called DocFields that form the basis of the columns in the database and the layout of the form. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColumnDescription
NameName of the record
OwnerCreator and Owner of the record
Created onDate and Time of Creation
Modified On Date and Time of Modification
DocstatusStatus of the record
+ 0 = Saved/Draft
+ 1 = Submitted
+ 2 = Cancelled/Deleted +
ParentName of the Parent
Parent TypeType of Parent
Parent FieldSpecifying the relationship with the parent (there can be multiple child relationships with the same DocType).
Index(idx)Index (sequence) of the record in the child table.
+ +#### Single DocType + +There are a certain type of DocTypes that are “Single”, i.e. they have no table associated and have only one record of its fields. DocTypes such as Global Defaults, Production Planning Tool are “Single” DocTypes. + +#### Field Columns + +In the fields table, there are many columns, here is an explanation of the columns of the field table. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColumnDescription
LabelField Label (that appears in the form).
TypeField Type
NameColumn name in the database, must be code friendly with no white spaces, special characters and capital letters.
optionsField settings:
+ For Select: List of options (each on a new line).
+ For Link: DocType that is “linked”.
+ For HTML: HTML Content +
Perm LevelPermission level (number) of the field. You can group fields by numbers, called levels, and apply rules on the levels.
WidthWidth of the field (in pixels) - useful for “Table” types.
ReqdChecked if field is mandatory (required).
In FilterChecked if field appears as a standard filter in old style reports.
HiddenChecked if field is hidden.
Print HideChecked if field is hidden in Print Formats.
Report HideChecked if field is hidden in old style reports.
Allow on SubmitChecked if this field can be edited after the document is “Submitted”.
Depends OnThe fieldname of the field that will decide whether this field will be shown or hidden. It is useful to hide un-necessary fields.
DescriptionDescription of the field
DefaultDefault value when a new record is created.
+ Note: “user” will set the current user as default and “today” will set today’s date (if the field is a Date field).
+ +#### Field Types and Options + +Here is a list of the different types of fields used to make / customize forms in ERPNext. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionOptions/Setting
DataSingle line text field with 180 characters
SelectSelect from a pre-determined items in a drop-down.The “Options” contains the drop-down items, each on a new row
LinkLink an existing document / recordOptions contains the name of the type of document (DocType)
CurrencyNumber with 2 decimal places, that will be shown separated by commas for thousands etc. in Print.e.g. 1,000,000.00
FloatNumber with 6 decimal places.e.g. 3.141593
IntInteger (no decimals)e.g. 100
DateDateFormat can be selected in Global Defaults
TimeTime
Text
TextMulti-line text box without formatting features
Text editorMulti-line text box with formatting toolbar etc
CodeCode EditorOptions can include the type of language for syntax formatting. + Eg JS / Python / HTML
Table (Grid)
TableTable of child items linked to the record.Options contains the name of the DocType of the child table. For example “Sales Invoice Item” for “Sales Invoice”
Layout
Section BreakBreak into a new horizontal section.The layout in ERPNext is evaluated from top to bottom.
Column BreakBreak into a new vertical column.
HTMLAdd a static text / help / link etc in HTMLOptions contains the HTML.
Action
ButtonButton[for developers only]
+ + + + + + + \ No newline at end of file diff --git a/docs/user/knowledge/docs.user.knowledge.markdown.md b/docs/user/knowledge/docs.user.knowledge.markdown.md new file mode 100644 index 00000000000..b2f76be5831 --- /dev/null +++ b/docs/user/knowledge/docs.user.knowledge.markdown.md @@ -0,0 +1,84 @@ +--- +{ + "_label": "Format Using Markdown" +} +--- + +Markdown is a simple way of writing text to format your content. Markdown allows you easy ways to format. + +1. Headings (h1 (largest), h2, h3, h4 and so on) +1. Paragraphs +1. Lists (numbered or bulleted) +1. Hyper links (links to other pages) +1. Images +1. Code +1. Embed HTML (HTML tags within your text) + +#### Headings + +Headings are specified by adding a `#` (hash) at the beginning of the line. The more the number of hashes, the smaller the heading: + + # This is a large heading. + + ### This is a smaller heading. + +#### Paragraphs + +To start a new paragraph, just make sure that there is an empty line at the beginning and end of the paragraph. + +To format text as **bold** or with _italics_ format as follows: + + **This text** is **bold** and _this one_ is with _italics_ + +#### Lists + +To define numbered lists, start your link with a number and a dot (.) and ensure there is a blank line before and after the list. The numbers are automatically generated so it does not matter what number you put: + + 1. list 1st item + 1. second item + 1. and so on + 1. and so forth + +To define bulleted lists, start your items with a hyphen (-) + + - item 1 + - item 2 + - item 3 + +To nest lists within one another, put four spaces to indent your inner list as follows: + + 1. item 1 + 1. item 2 + - sub item 1 + - sub item 2 + 1. item 3 + +#### Links (to other pages) + +Links to other pages can be defined by adding your text in box brackets [] followed by the link in round brackets () + + [This is an external link](http://example.com) + [A link within the site](my-page.html) + +#### Images + +Images can be added by adding an exclamation ! before the link. + + ![A flower](files/flower.gif) + + +#### Code + +To add a code block, just leave a blank line before and after the block and make sure all code line are indented by four spaces: + + This is normal text + + This is a code block + +#### HTML + +You can embed any kind of HTML tags within your code. Any content written within HTML tags will not be formatted. + +[Detailed description of the markdown format](http://daringfireball.net/projects/markdown/syntax) + + diff --git a/docs/user/knowledge/docs.user.knowledge.md b/docs/user/knowledge/docs.user.knowledge.md index 5d98cdd4596..11dc9957ac1 100644 --- a/docs/user/knowledge/docs.user.knowledge.md +++ b/docs/user/knowledge/docs.user.knowledge.md @@ -4,8 +4,12 @@ "_toc": [ "docs.user.knowledge.fiscal_year", "docs.user.knowledge.accounting", - "docs.user.knowledge.accounting_entries" + "docs.user.knowledge.accounting_entries", + "docs.user.knowledge.doctype", + "docs.user.knowledge.attachment_csv", + "docs.user.knowledge.markdown" ] } --- -Knowledge Library contains definitions and explanations of various management concepts. This page is created for users who wish to elaborate their conceptual knowledge. \ No newline at end of file +Knowledge Library contains definitions and explanations of various management concepts. This page is created for users who wish to elaborate their conceptual knowledge. + diff --git a/docs/user/selling/docs.user.selling.customer.md b/docs/user/selling/docs.user.selling.customer.md index 108f20da831..5336ac61a11 100644 --- a/docs/user/selling/docs.user.selling.customer.md +++ b/docs/user/selling/docs.user.selling.customer.md @@ -4,7 +4,9 @@ "_title_image": "img/customers.png" } --- -You can either directly create your Customers via +A customer, who is sometimes known as a client, buyer, or purchaser is the one who receives goods, services, products, or ideas, from a seller for a monetary consideration. A customer can also receive goods or services from a vendor or a supplier for other valuable considerations. + + You can either directly create your Customers via > Selling > Customer diff --git a/docs/user/setup/docs.user.setup.data_import.md b/docs/user/setup/docs.user.setup.data_import.md index eec6b65e453..b40573ed43a 100644 --- a/docs/user/setup/docs.user.setup.data_import.md +++ b/docs/user/setup/docs.user.setup.data_import.md @@ -3,7 +3,7 @@ "_label": "Data Import Tool" } --- -The Data Import Tool is a great way to upload (or edit) bulk data, specially master data, into the system. To start the tool go to: +The Data Import Tool is a great way to upload (or edit) bulk data, specially master data, into the system. To Open the data import tool, you either go to Setup or go to the Transaction you want to Import. If Data Import is allowed, you will see an Import Button: @@ -15,7 +15,7 @@ The tool has two sections, one to download a template and the second to upload t ### 1. Downloading The Template -Data in ERPNext is stored in tables, much like a spreadsheet with columns and rows of data. Each entity in ERPNext can have multiple child tables associated with it too. The child tables are linked to the parent tables and are implemented where are multiple values for any property. For example an Item can have multiple prices, An Invoice has multiple Items and so on. +Data in ERPNext is stored in tables, much like a spreadsheet with columns and rows of data. Each entity in ERPNext can have multiple child tables associated with it too. The child tables are linked to the parent tables and are implemented where there are multiple values for any property. For example an Item can have multiple prices, An Invoice has multiple Items and so on. You can import each table separately, or all at a time. In the child table, you must mention the parent of the row in the “parent” column so that ERPNext knows which Item’s price or tax you are trying to set if you are importing separately. @@ -36,7 +36,7 @@ Then export your template or save it as a **Comma Separated Values** (CSV) file. ### 3. Upload the .csv File -Finally attach the .csv file in the section section click on the "Upload and Import" button. +Finally attach the .csv file in the section. Click on the "Upload and Import" button. ![Attach and Upload](img/import-5.png) diff --git a/docs/user/stock/docs.user.stock.item.md b/docs/user/stock/docs.user.stock.item.md index c98afd29dd8..5ac78d7e51a 100644 --- a/docs/user/stock/docs.user.stock.item.md +++ b/docs/user/stock/docs.user.stock.item.md @@ -27,11 +27,7 @@ To upload an image for your icon that will appear in all transactions, save the ![Item Properties](img/item-add-image.png) -### Item Pricing - -Item Price and Price Lists: ERPNext lets you maintain multiple selling prices for an Item using Price Lists. A Price List is a place where different rate plans can be stored. It’s a name you can give to a set of Item prices. In case you have different zones (based on the shipping costs), for different currencies etc, you can maintain different Price Lists. A Price List is formed when you create different Item Prices. To import Item Price see [Importing Data](docs.user.data_import.md). - -## Inventory : Warehouse and Stock Setting +### Inventory : Warehouse and Stock Setting In ERPNext, you can select different type of Warehouses to stock your different Items. This can be selected based on Item types. It could be Fixed Asset Item, Stock Item or even Manufacturing Item. diff --git a/docs/user/stock/docs.user.stock.md b/docs/user/stock/docs.user.stock.md index 08f16f84308..a8c74c9175c 100644 --- a/docs/user/stock/docs.user.stock.md +++ b/docs/user/stock/docs.user.stock.md @@ -9,6 +9,7 @@ "docs.user.stock.purchase_receipt", "docs.user.stock.delivery_note", "docs.user.stock.stock_entry", + "docs.user.stock.opening_stock", "docs.user.stock.material_issue", "docs.user.stock.sales_return", "docs.user.stock.purchase_return", diff --git a/hr/doctype/employee/employee.js b/hr/doctype/employee/employee.js index 05aec3efa34..01200e7d22c 100644 --- a/hr/doctype/employee/employee.js +++ b/hr/doctype/employee/employee.js @@ -14,6 +14,7 @@ erpnext.hr.EmployeeController = wn.ui.form.Controller.extend({ onload: function() { this.frm.toggle_display(["esic_card_no", "gratuity_lic_id", "pan_number", "pf_number"], wn.control_panel.country==="India"); + if(this.frm.doc.__islocal) this.frm.set_value("employee_name", ""); }, refresh: function() { diff --git a/hr/doctype/expense_claim/expense_claim.js b/hr/doctype/expense_claim/expense_claim.js index 65c557947f8..f7895b10eb0 100644 --- a/hr/doctype/expense_claim/expense_claim.js +++ b/hr/doctype/expense_claim/expense_claim.js @@ -58,11 +58,12 @@ cur_frm.cscript.onload = function(doc,cdt,cdn) { query:"controllers.queries.employee_query" } } - + var exp_approver = doc.exp_approver; return cur_frm.call({ method:"hr.utils.get_expense_approver_list", callback: function(r) { cur_frm.set_df_property("exp_approver", "options", r.message); + if(exp_approver) cur_frm.set_value("exp_approver", exp_approver); } }); } @@ -79,26 +80,39 @@ cur_frm.cscript.clear_sanctioned = function(doc) { } cur_frm.cscript.refresh = function(doc,cdt,cdn){ + cur_frm.cscript.set_help(doc); + + if(!doc.__islocal) { + cur_frm.toggle_enable("exp_approver", (doc.owner==user && doc.approval_status=="Draft")); + cur_frm.toggle_enable("approval_status", (doc.exp_approver==user && doc.docstatus==0)); + + if(!doc.__islocal && user!=doc.exp_approver && cur_frm.frm_head.appframe.buttons.Submit) + cur_frm.frm_head.appframe.buttons.Submit.toggle(false); + + if(doc.docstatus==0 && doc.exp_approver==user && doc.approval_status=="Approved") + cur_frm.savesubmit(); + + if(doc.docstatus==1 && wn.model.can_create("Journal Voucher")) + cur_frm.add_custom_button("Make Bank Voucher", cur_frm.cscript.make_bank_voucher); + } +} + +cur_frm.cscript.set_help = function(doc) { cur_frm.set_intro(""); if(doc.__islocal && !in_list(user_roles, "HR User")) { cur_frm.set_intro("Fill the form and save it") } else { if(doc.docstatus==0 && doc.approval_status=="Draft") { if(user==doc.exp_approver) { - cur_frm.set_intro("You are the Expense Approver for this record. Please Update the 'Status' and Save"); - cur_frm.toggle_enable("approval_status", true); + cur_frm.set_intro("You are the Expense Approver for this record. \ + Please Update the 'Status' and Save"); } else { - cur_frm.set_intro("Expense Claim is pending approval. Only the Expense Approver can update status."); - cur_frm.toggle_enable("approval_status", false); - if(!doc.__islocal && cur_frm.frm_head.appframe.buttons.Submit) - cur_frm.frm_head.appframe.buttons.Submit.toggle(false); + cur_frm.set_intro("Expense Claim is pending approval. \ + Only the Expense Approver can update status."); } } else { if(doc.approval_status=="Approved") { cur_frm.set_intro("Expense Claim has been approved."); - if(doc.docstatus==0) cur_frm.savesubmit(); - if(doc.docstatus==1) cur_frm.add_custom_button("Make Bank Voucher", - cur_frm.cscript.make_bank_voucher); } else if(doc.approval_status=="Rejected") { cur_frm.set_intro("Expense Claim has been rejected."); } diff --git a/hr/doctype/leave_application/leave_application.js b/hr/doctype/leave_application/leave_application.js index 4c7d356c159..5969f4dc4fb 100755 --- a/hr/doctype/leave_application/leave_application.js +++ b/hr/doctype/leave_application/leave_application.js @@ -10,6 +10,8 @@ cur_frm.cscript.onload = function(doc, dt, dn) { cur_frm.set_value("status", "Open"); cur_frm.cscript.calculate_total_days(doc, dt, dn); } + + var leave_approver = doc.leave_approver; return cur_frm.call({ method:"hr.utils.get_leave_approver_list", callback: function(r) { @@ -17,6 +19,7 @@ cur_frm.cscript.onload = function(doc, dt, dn) { function(profile) { return {value: profile, label: wn.user_info(profile).fullname}; })); + if(leave_approver) cur_frm.set_value("leave_approver", leave_approver); cur_frm.cscript.get_leave_balance(cur_frm.doc); } }); diff --git a/patches/august_2013/p02_rename_price_list.py b/patches/august_2013/p02_rename_price_list.py index 41efb273069..0a1929925ba 100644 --- a/patches/august_2013/p02_rename_price_list.py +++ b/patches/august_2013/p02_rename_price_list.py @@ -6,6 +6,7 @@ import webnotes def execute(): webnotes.reload_doc("selling", "doctype", "shopping_cart_price_list") + webnotes.reload_doc("setup", "doctype", "item_price") for t in [ ("Supplier Quotation", "price_list_name", "buying_price_list"), diff --git a/patches/october_2013/__init__.py b/patches/october_2013/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/patches/october_2013/fix_is_cancelled_in_sle.py b/patches/october_2013/fix_is_cancelled_in_sle.py new file mode 100644 index 00000000000..cb51b5d7ca1 --- /dev/null +++ b/patches/october_2013/fix_is_cancelled_in_sle.py @@ -0,0 +1,13 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import webnotes + +def execute(): + webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled = 'No' + where ifnull(is_cancelled, '') = ''""") + + webnotes.conn.sql("""update tabBin b set b.stock_uom = + (select i.stock_uom from tabItem i where i.name = b.item_code) + where b.creation>='2013-09-01'""") \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 7041ba88585..f228acf97e7 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -218,4 +218,6 @@ patch_list = [ "execute:webnotes.bean('Style Settings').save() #2013-09-19", "execute:webnotes.conn.set_value('Accounts Settings', None, 'frozen_accounts_modifier', 'Accounts Manager') # 2013-09-24", "patches.september_2013.p04_unsubmit_serial_nos", + "patches.september_2013.p05_fix_customer_in_pos", + "patches.october_2013.fix_is_cancelled_in_sle", ] \ No newline at end of file diff --git a/patches/september_2013/p05_fix_customer_in_pos.py b/patches/september_2013/p05_fix_customer_in_pos.py new file mode 100644 index 00000000000..60210dab24e --- /dev/null +++ b/patches/september_2013/p05_fix_customer_in_pos.py @@ -0,0 +1,22 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import webnotes +def execute(): + si_list = webnotes.conn.sql("""select name, debit_to from `tabSales Invoice` + where ifnull(is_pos, 1)=1 and docstatus=1 and modified > '2013-09-03'""", as_dict=1) + + for si in si_list: + if not webnotes.conn.get_value("GL Entry", {"voucher_type": "Sales Invoice", + "voucher_no": si.name, "account": si.debit_to}): + debit_to = webnotes.conn.sql("""select account from `tabGL Entry` gle + where voucher_type='Sales Invoice' and voucher_no=%s + and (select master_type from tabAccount where name=gle.account)='Customer' + """, si.name) + if debit_to: + si_bean = webnotes.bean("Sales Invoice", si.name) + si_bean.doc.debit_to = debit_to[0][0] + si_bean.doc.customer = None + si_bean.run_method("set_customer_defaults") + si_bean.update_after_submit() \ No newline at end of file diff --git a/portal/templates/includes/cart.js b/portal/templates/includes/cart.js index 5ff6e3fb72f..bd3f56533c5 100644 --- a/portal/templates/includes/cart.js +++ b/portal/templates/includes/cart.js @@ -9,7 +9,6 @@ $(document).ready(function() { type: "POST", method: "selling.utils.cart.get_cart_quotation", callback: function(r) { - console.log(r); $("#cart-container").removeClass("hide"); $(".progress").remove(); if(r.exc) { @@ -126,8 +125,8 @@ $.extend(erpnext.cart, { }, render_item_row: function($cart_items, doc) { - doc.image_html = doc.image ? - '
' : + doc.image_html = doc.website_image ? + '
' : '{% include "app/stock/doctype/item/templates/includes/product_missing_image.html" %}'; if(doc.description === doc.item_name) doc.description = ""; diff --git a/projects/doctype/time_log/time_log.js b/projects/doctype/time_log/time_log.js index 7a03955e2a9..ab21a3151d9 100644 --- a/projects/doctype/time_log/time_log.js +++ b/projects/doctype/time_log/time_log.js @@ -9,4 +9,6 @@ erpnext.projects.TimeLog = wn.ui.form.Controller.extend({ } }); -cur_frm.cscript = new erpnext.projects.TimeLog({frm: cur_frm}); \ No newline at end of file +cur_frm.cscript = new erpnext.projects.TimeLog({frm: cur_frm}); + +cur_frm.add_fetch('task','project','project'); \ No newline at end of file diff --git a/public/js/startup.css b/public/js/startup.css index ab70ee44147..c3b7276de4e 100644 --- a/public/js/startup.css +++ b/public/js/startup.css @@ -29,4 +29,21 @@ span, div, td, input, textarea, button, select { width: 32px; height: 32px; margin: -10px auto; +} + +/* pos */ +.pos-item { + height: 200px; + overflow: hidden; + cursor: pointer; + padding-left: 5px !important; + padding-right: 5px !important; +} + +.pos-bill { + padding: 20px 5px; + font-family: Monospace; + border: 1px solid #eee; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); } \ No newline at end of file diff --git a/public/js/stock_grid_report.js b/public/js/stock_grid_report.js index 8b79b5e1eea..46370d27f60 100644 --- a/public/js/stock_grid_report.js +++ b/public/js/stock_grid_report.js @@ -29,6 +29,8 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ if(add_qty) wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]); + + if(sl.serial_no) value_diff = this.get_serialized_value_diff(sl); } else { // outgoing if(sl.serial_no) { @@ -98,7 +100,7 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ $.each(sl.serial_no.trim().split("\n"), function(i, sr) { if(sr) { - value_diff += flt(me.serialized_buying_rates[sr.trim()]); + value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]); } }); @@ -112,7 +114,7 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ if(sle.qty > 0 && sle.serial_no) { $.each(sle.serial_no.trim().split("\n"), function(i, sr) { if(sr && sle.incoming_rate !== undefined) { - serialized_buying_rates[sr.trim()] = flt(sle.incoming_rate); + serialized_buying_rates[sr.trim().toLowerCase()] = flt(sle.incoming_rate); } }); } diff --git a/public/js/transaction.js b/public/js/transaction.js index 387140472c7..e12d1084e98 100644 --- a/public/js/transaction.js +++ b/public/js/transaction.js @@ -62,6 +62,56 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ erpnext.hide_company(); 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.pos_btn(); + }, + + pos_btn: function() { + if(this.$pos_btn) + this.$pos_btn.remove(); + + if(!this.pos_active) { + var btn_label = wn._("POS View"), + icon = "icon-desktop"; + } else { + var btn_label = wn._(this.frm.doctype) + wn._(" View"), + icon = "icon-file-text"; + } + var me = this; + + this.$pos_btn = this.frm.add_custom_button(btn_label, function() { + me.toggle_pos(); + me.pos_btn(); + }, icon); + }, + + toggle_pos: function(show) { + // Check whether it is Selling or Buying cycle + var price_list = wn.meta.has_field(cur_frm.doc.doctype, "selling_price_list") ? + this.frm.doc.selling_price_list : this.frm.doc.buying_price_list; + + if (!price_list) + msgprint(wn._("Please select Price List")) + else { + if((show===true && this.pos_active) || (show===false && !this.pos_active)) return; + + // make pos + if(!this.frm.pos) { + this.frm.layout.add_view("pos"); + this.frm.pos = new erpnext.POS(this.frm.layout.views.pos, this.frm); + } + + // toggle view + this.frm.layout.set_view(this.pos_active ? "" : "pos"); + this.pos_active = !this.pos_active; + + // refresh + if(this.pos_active) + this.frm.pos.refresh(); + this.frm.refresh(); + } }, validate: function() { @@ -182,6 +232,29 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ tax_rate: function(doc, cdt, cdn) { this.calculate_taxes_and_totals(); }, + + // serial_no: function(doc, cdt, cdn) { + // var me = this; + // var item = wn.model.get_doc(cdt, cdn); + // if (!item.item_code) { + // wn.call({ + // method: 'accounts.doctype.sales_invoice.pos.get_item_from_serial_no', + // args: {serial_no: this.serial_no.$input.val()}, + // callback: function(r) { + // if (r.message) { + // var item_code = r.message[0].item_code; + // var child = wn.model.add_child(me.frm.doc, this.frm.doctype + " Item", + // this.frm.cscript.fname); + // child.item_code = item_code; + // me.frm.cscript.item_code(me.frm.doc, child.doctype, child.name); + // } + // else + // msgprint(wn._("Invalid Serial No.")); + // me.refresh(); + // } + // }); + // } + // }, row_id: function(doc, cdt, cdn) { var tax = wn.model.get_doc(cdt, cdn); @@ -418,10 +491,10 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ (this.frm.doc.currency != company_currency && this.frm.doc.conversion_rate != 1.0)) : false; - // if(!valid_conversion_rate) { - // wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) + - // " 1 " + this.frm.doc.currency + " = [?] " + company_currency); - // } + if(!valid_conversion_rate) { + wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) + + " 1 " + this.frm.doc.currency + " = [?] " + company_currency); + } }, calculate_taxes_and_totals: function() { diff --git a/selling/doctype/customer/customer.js b/selling/doctype/customer/customer.js index 4e2f42c7340..d2c632530b3 100644 --- a/selling/doctype/customer/customer.js +++ b/selling/doctype/customer/customer.js @@ -45,7 +45,7 @@ cur_frm.cscript.setup_dashboard = function(doc) { cur_frm.dashboard.reset(doc); if(doc.__islocal) return; - cur_frm.dashboard.set_headline('Loading...') + cur_frm.dashboard.set_headline(''+ wn._('Loading...')+ '') cur_frm.dashboard.add_doctype_badge("Opportunity", "customer"); cur_frm.dashboard.add_doctype_badge("Quotation", "customer"); @@ -99,7 +99,7 @@ cur_frm.cscript.make_contact = function() { return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where customer='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_contact desc" }, as_dict: 1, - no_results_message: 'No contacts created', + no_results_message: wn._('No contacts created'), render_row: cur_frm.cscript.render_contact_row, }); // note: render_contact_row is defined in contact_control.js @@ -119,4 +119,4 @@ cur_frm.fields_dict.lead_name.get_query = function(doc,cdt,cdn) { return{ query:"controllers.queries.lead_query" } -} \ No newline at end of file +} diff --git a/selling/doctype/lead/lead.js b/selling/doctype/lead/lead.js index a3ae7838672..3c6fda7922c 100644 --- a/selling/doctype/lead/lead.js +++ b/selling/doctype/lead/lead.js @@ -33,16 +33,7 @@ erpnext.LeadController = wn.ui.form.Controller.extend({ var doc = this.frm.doc; erpnext.hide_naming_series(); this.frm.clear_custom_buttons(); - - this.frm.dashboard.reset(doc); - if(!doc.__islocal) { - if(doc.status=="Converted") { - this.frm.dashboard.set_headline_alert(wn._("Converted"), "alert-success", "icon-ok-sign"); - } else { - this.frm.dashboard.set_headline_alert(wn._(doc.status), "alert-info", "icon-exclamation-sign"); - } - } - + this.frm.__is_customer = this.frm.__is_customer || this.frm.doc.__is_customer; if(!this.frm.doc.__islocal && !this.frm.__is_customer) { this.frm.add_custom_button("Create Customer", this.create_customer); diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py index eb3c69571a3..7a37446bf1b 100644 --- a/selling/doctype/lead/lead.py +++ b/selling/doctype/lead/lead.py @@ -125,6 +125,10 @@ def make_opportunity(source_name, target_doclist=None): "campaign_name": "campaign", "doctype": "enquiry_from", "name": "lead", + "lead_name": "contact_display", + "company_name": "customer_name", + "email_id": "contact_email", + "mobile_no": "contact_mobile" } }}, target_doclist) diff --git a/selling/doctype/lead/lead.txt b/selling/doctype/lead/lead.txt index 9439e7854f8..6044f7994b4 100644 --- a/selling/doctype/lead/lead.txt +++ b/selling/doctype/lead/lead.txt @@ -2,7 +2,7 @@ { "creation": "2013-04-10 11:45:37", "docstatus": 0, - "modified": "2013-10-02 14:24:34", + "modified": "2013-10-02 14:24:30", "modified_by": "Administrator", "owner": "Administrator" }, @@ -112,7 +112,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nOpen\nReplied\nAttempted to Contact\nContact in Future\nContacted\nInterested\nNot interested\nLead Lost\nConverted\nPassive", + "options": "\nOpen\nReplied\nAttempted to Contact\nContact in Future\nContacted\nOpportunity Made\nInterested\nNot interested\nLead Lost\nConverted\nPassive", "reqd": 1, "search_index": 1 }, diff --git a/selling/doctype/opportunity/opportunity.js b/selling/doctype/opportunity/opportunity.js index 1ec3f3fc4c0..25f28bf3fbf 100644 --- a/selling/doctype/opportunity/opportunity.js +++ b/selling/doctype/opportunity/opportunity.js @@ -53,6 +53,11 @@ erpnext.selling.Opportunity = wn.ui.form.Controller.extend({ this.frm.set_query("contact_by", erpnext.queries.profile); } + this.frm.set_query("customer_address", function() { + if(me.frm.doc.lead) return {filters: { lead: me.frm.doc.lead } }; + else if(me.frm.doc.customer) return {filters: { customer: me.frm.doc.customer } }; + }); + this.frm.set_query("item_code", "enquiry_details", function() { return { query: "controllers.queries.item_query", @@ -63,7 +68,6 @@ erpnext.selling.Opportunity = wn.ui.form.Controller.extend({ $.each([["lead", "lead"], ["customer", "customer"], - ["customer_address", "customer_filter"], ["contact_person", "customer_filter"], ["territory", "not_a_group_filter"]], function(i, opts) { me.frm.set_query(opts[0], erpnext.queries[opts[1]]); @@ -151,8 +155,14 @@ cur_frm.cscript.lead_cust_show = function(doc,cdt,cdn){ } } -cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { - if(doc.customer) return get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); +cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc, dt, dn) { + args = { + address: doc.customer_address, + contact: doc.contact_person + } + if(doc.customer) args.update({customer: doc.customer}); + + return get_server_fields('get_customer_address', JSON.stringify(args),'', doc, dt, dn, 1); } cur_frm.cscript.lead = function(doc, cdt, cdn) { @@ -163,7 +173,7 @@ cur_frm.cscript.lead = function(doc, cdt, cdn) { source_name: cur_frm.doc.lead }) - unhide_field(['customer_name', 'address_display','contact_mobile', + unhide_field(['customer_name', 'address_display','contact_mobile', 'customer_address', 'contact_email', 'territory']); } diff --git a/selling/doctype/opportunity/opportunity.py b/selling/doctype/opportunity/opportunity.py index 284c5e0545c..e0be5fa9577 100644 --- a/selling/doctype/opportunity/opportunity.py +++ b/selling/doctype/opportunity/opportunity.py @@ -127,12 +127,12 @@ class DocType(TransactionBase): from accounts.utils import validate_fiscal_year validate_fiscal_year(self.doc.transaction_date, self.doc.fiscal_year, "Opportunity Date") - - if not self.doc.status: - self.doc.status = "Draft" + self.doc.status = "Draft" def on_submit(self): webnotes.conn.set(self.doc, 'status', 'Submitted') + if self.doc.lead and webnotes.conn.get_value("Lead", self.doc.lead, "status")!="Converted": + webnotes.conn.set_value("Lead", self.doc.lead, "status", "Opportunity Made") def on_cancel(self): chk = webnotes.conn.sql("select t1.name from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.docstatus=1 and (t1.status!='Order Lost' and t1.status!='Cancelled') and t2.prevdoc_docname = %s",self.doc.name) @@ -141,6 +141,14 @@ class DocType(TransactionBase): raise Exception else: webnotes.conn.set(self.doc, 'status', 'Cancelled') + if self.doc.lead and webnotes.conn.get_value("Lead", self.doc.lead, + "status")!="Converted": + if webnotes.conn.get_value("Communication", {"parent": self.doc.lead}): + status = "Contacted" + else: + status = "Open" + + webnotes.conn.set_value("Lead", self.doc.lead, "status", status) def declare_enquiry_lost(self,arg): chk = webnotes.conn.sql("select t1.name from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.docstatus=1 and (t1.status!='Order Lost' and t1.status!='Cancelled') and t2.prevdoc_docname = %s",self.doc.name) diff --git a/selling/doctype/opportunity/opportunity.txt b/selling/doctype/opportunity/opportunity.txt index d39b3efdae1..21eae5f7234 100644 --- a/selling/doctype/opportunity/opportunity.txt +++ b/selling/doctype/opportunity/opportunity.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 18:50:30", "docstatus": 0, - "modified": "2013-10-02 14:24:35", + "modified": "2013-10-02 14:24:30", "modified_by": "Administrator", "owner": "Administrator" }, @@ -189,34 +189,16 @@ "options": "icon-bullhorn", "read_only": 0 }, - { - "doctype": "DocField", - "fieldname": "contact_person", - "fieldtype": "Link", - "in_filter": 1, - "label": "Contact Person", - "options": "Contact", - "print_hide": 1, - "read_only": 0 - }, { "doctype": "DocField", "fieldname": "customer_address", "fieldtype": "Link", "in_filter": 1, - "label": "Customer Address", + "label": "Customer / Lead Address", "options": "Address", "print_hide": 1, "read_only": 0 }, - { - "doctype": "DocField", - "fieldname": "customer_name", - "fieldtype": "Data", - "label": "Customer Name", - "print_hide": 0, - "read_only": 1 - }, { "doctype": "DocField", "fieldname": "address_display", @@ -227,12 +209,60 @@ "oldfieldtype": "Small Text", "read_only": 1 }, + { + "description": "To manage Territory, click here", + "doctype": "DocField", + "fieldname": "territory", + "fieldtype": "Link", + "in_filter": 1, + "label": "Territory", + "options": "Territory", + "print_hide": 1, + "read_only": 0, + "reqd": 0, + "search_index": 1 + }, + { + "depends_on": "eval:doc.enquiry_from==\"Customer\"", + "description": "To manage Territory, click here", + "doctype": "DocField", + "fieldname": "customer_group", + "fieldtype": "Link", + "hidden": 0, + "in_filter": 1, + "label": "Customer Group", + "oldfieldname": "customer_group", + "oldfieldtype": "Link", + "options": "Customer Group", + "print_hide": 1, + "read_only": 0, + "reqd": 0, + "search_index": 1 + }, { "doctype": "DocField", "fieldname": "column_break3", "fieldtype": "Column Break", "read_only": 0 }, + { + "doctype": "DocField", + "fieldname": "customer_name", + "fieldtype": "Data", + "label": "Customer Name", + "print_hide": 0, + "read_only": 1 + }, + { + "doctype": "DocField", + "fieldname": "contact_person", + "fieldtype": "Link", + "in_filter": 1, + "label": "Contact Person", + "options": "Contact", + "print_hide": 1, + "read_only": 0 + }, { "doctype": "DocField", "fieldname": "contact_display", @@ -254,36 +284,6 @@ "label": "Contact Mobile No", "read_only": 1 }, - { - "depends_on": "eval:doc.enquiry_from==\"Customer\"", - "description": "To manage Territory, click here", - "doctype": "DocField", - "fieldname": "customer_group", - "fieldtype": "Link", - "hidden": 0, - "in_filter": 1, - "label": "Customer Group", - "oldfieldname": "customer_group", - "oldfieldtype": "Link", - "options": "Customer Group", - "print_hide": 1, - "read_only": 0, - "reqd": 0, - "search_index": 1 - }, - { - "description": "To manage Territory, click here", - "doctype": "DocField", - "fieldname": "territory", - "fieldtype": "Link", - "in_filter": 1, - "label": "Territory", - "options": "Territory", - "print_hide": 1, - "read_only": 0, - "reqd": 0, - "search_index": 1 - }, { "description": "Filing in Additional Information about the Opportunity will help you analyze your data better.", "doctype": "DocField", diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js index ea1b62aa0a7..e20308f18ee 100644 --- a/selling/doctype/quotation/quotation.js +++ b/selling/doctype/quotation/quotation.js @@ -11,6 +11,7 @@ cur_frm.cscript.sales_team_fname = "sales_team"; wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); wn.require('app/selling/doctype/sales_common/sales_common.js'); +wn.require('app/accounts/doctype/sales_invoice/pos.js'); erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ onload: function(doc, dt, dn) { @@ -82,12 +83,12 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ }, validate_company_and_party: function(party_field) { - if(this.frm.doc.quotation_to == "Lead") { - return true; - } else if(!this.frm.doc.quotation_to) { + if(!this.frm.doc.quotation_to) { msgprint(wn._("Please select a value for" + " " + wn.meta.get_label(this.frm.doc.doctype, "quotation_to", this.frm.doc.name))); return false; + } else if (this.frm.doc.quotation_to == "Lead") { + return true; } else { return this._super(party_field); } diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index dc58377e4bf..c87e823dbab 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -162,8 +162,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ var item = wn.model.get_doc(cdt, cdn); if(item.item_code || item.barcode) { if(!this.validate_company_and_party("customer")) { - item.item_code = null; - refresh_field("item_code", item.name, item.parentfield); + cur_frm.fields_dict[me.frm.cscript.fname].grid.grid_rows[item.idx - 1].remove(); } else { return this.frm.call({ method: "selling.utils.get_item_details", diff --git a/selling/doctype/sales_order/sales_order.js b/selling/doctype/sales_order/sales_order.js index 33699f05d84..0c261793e15 100644 --- a/selling/doctype/sales_order/sales_order.js +++ b/selling/doctype/sales_order/sales_order.js @@ -12,6 +12,7 @@ cur_frm.cscript.sales_team_fname = "sales_team"; wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/accounts/doctype/sales_invoice/pos.js'); erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({ refresh: function(doc, dt, dn) { @@ -26,34 +27,34 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( cur_frm.dashboard.add_progress(cint(doc.per_billed) + wn._("% Billed"), doc.per_billed); - cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + cur_frm.add_custom_button(wn._('Send SMS'), cur_frm.cscript.send_sms); // delivery note if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales') - cur_frm.add_custom_button('Make Delivery', this.make_delivery_note); + cur_frm.add_custom_button(wn._('Make Delivery'), this.make_delivery_note); // maintenance if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) { - cur_frm.add_custom_button('Make Maint. Visit', this.make_maintenance_visit); - cur_frm.add_custom_button('Make Maint. Schedule', + cur_frm.add_custom_button(wn._('Make Maint. Visit'), this.make_maintenance_visit); + cur_frm.add_custom_button(wn._('Make Maint. Schedule'), this.make_maintenance_schedule); } // indent if(!doc.order_type || (doc.order_type == 'Sales')) - cur_frm.add_custom_button('Make ' + wn._('Material Request'), + cur_frm.add_custom_button(wn._('Make ') + wn._('Material Request'), this.make_material_request); // sales invoice if(flt(doc.per_billed, 2) < 100) - cur_frm.add_custom_button('Make Invoice', this.make_sales_invoice); + cur_frm.add_custom_button(wn._('Make Invoice'), this.make_sales_invoice); // stop if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100) - cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']); + cur_frm.add_custom_button(wn._('Stop!'), cur_frm.cscript['Stop Sales Order']); } else { // un-stop - cur_frm.dashboard.set_headline_alert("Stopped", "alert-danger", "icon-stop"); - cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']); + cur_frm.dashboard.set_headline_alert(wn._("Stopped"), "alert-danger", "icon-stop"); + cur_frm.add_custom_button(wn._('Unstop'), cur_frm.cscript['Unstop Sales Order']); } } @@ -157,7 +158,7 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) { cur_frm.cscript['Stop Sales Order'] = function() { var doc = cur_frm.doc; - var check = confirm("Are you sure you want to STOP " + doc.name); + var check = confirm(wn._("Are you sure you want to STOP ") + doc.name); if (check) { return $c('runserverobj', { @@ -172,7 +173,7 @@ cur_frm.cscript['Stop Sales Order'] = function() { cur_frm.cscript['Unstop Sales Order'] = function() { var doc = cur_frm.doc; - var check = confirm("Are you sure you want to UNSTOP " + doc.name); + var check = confirm(wn._("Are you sure you want to UNSTOP ") + doc.name); if (check) { return $c('runserverobj', { @@ -188,4 +189,4 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) { if(cint(wn.boot.notification_settings.sales_order)) { cur_frm.email_doc(wn.boot.notification_settings.sales_order_message); } -}; \ No newline at end of file +}; diff --git a/selling/utils/__init__.py b/selling/utils/__init__.py index 224944dd884..801d82bf40e 100644 --- a/selling/utils/__init__.py +++ b/selling/utils/__init__.py @@ -34,6 +34,7 @@ def get_item_details(args): "plc_conversion_rate": 1.0 } """ + if isinstance(args, basestring): args = json.loads(args) args = webnotes._dict(args) diff --git a/selling/utils/cart.py b/selling/utils/cart.py index 92d37ea969e..e48059d50e3 100644 --- a/selling/utils/cart.py +++ b/selling/utils/cart.py @@ -286,7 +286,7 @@ def apply_cart_settings(party=None, quotation=None): cart_settings = webnotes.get_obj("Shopping Cart Settings") billing_territory = get_address_territory(quotation.doc.customer_address) or \ - party.territory + party.territory or "All Territories" set_price_list_and_rate(quotation, cart_settings, billing_territory) diff --git a/setup/doctype/backup_manager/backup_dropbox.py b/setup/doctype/backup_manager/backup_dropbox.py index 8d163539522..b4a8f66d4c0 100644 --- a/setup/doctype/backup_manager/backup_dropbox.py +++ b/setup/doctype/backup_manager/backup_dropbox.py @@ -96,6 +96,7 @@ def backup_to_dropbox(): error_log = [] path = os.path.join(get_base_path(), "public", "files") for filename in os.listdir(path): + filename = cstr(filename) if filename in ignore_list: continue diff --git a/setup/doctype/backup_manager/backup_googledrive.py b/setup/doctype/backup_manager/backup_googledrive.py index daf852c4254..c72295ae627 100644 --- a/setup/doctype/backup_manager/backup_googledrive.py +++ b/setup/doctype/backup_manager/backup_googledrive.py @@ -85,6 +85,7 @@ def backup_to_gdrive(): webnotes.conn.close() path = os.path.join(get_base_path(), "public", "files") for filename in os.listdir(path): + filename = cstr(filename) found = False filepath = os.path.join(path, filename) ext = filename.split('.')[-1] diff --git a/setup/doctype/company/charts/import_from_openerp.py b/setup/doctype/company/charts/import_from_openerp.py index 894c2d739d8..ef800086c94 100644 --- a/setup/doctype/company/charts/import_from_openerp.py +++ b/setup/doctype/company/charts/import_from_openerp.py @@ -9,6 +9,7 @@ from __future__ import unicode_literals import os, json from xml.etree import ElementTree as ET from webnotes.utils.datautils import read_csv_content +from webnotes.utils import cstr path = "/Users/rmehta/Downloads/openerp/openerp/addons" chart_roots = [] @@ -108,6 +109,7 @@ def find_charts(): basename = os.path.basename(basepath) if basename.startswith("l10n"): for fname in files: + fname = cstr(fname) filepath = os.path.join(basepath, fname) if fname.endswith(".xml"): tree = ET.parse(filepath) diff --git a/setup/doctype/price_list/price_list.css b/setup/doctype/price_list/price_list.css new file mode 100644 index 00000000000..61b069442f8 --- /dev/null +++ b/setup/doctype/price_list/price_list.css @@ -0,0 +1,7 @@ +.table-grid tbody tr { + cursor: pointer; +} + +.table-grid thead tr { + height: 50px; +} \ No newline at end of file diff --git a/setup/doctype/price_list/price_list.js b/setup/doctype/price_list/price_list.js index f3adc727579..67090bcbc16 100644 --- a/setup/doctype/price_list/price_list.js +++ b/setup/doctype/price_list/price_list.js @@ -5,4 +5,253 @@ $.extend(cur_frm.cscript, { onload: function() { erpnext.add_for_territory(); }, +}); + +cur_frm.cscript.refresh = function(doc, cdt, cdn) { + cur_frm.cscript.show_item_prices(); +} + +cur_frm.cscript.show_item_prices = function() { + var item_price = wn.model.get("Item Price", {parent: cur_frm.doc.name}); + + $(cur_frm.fields_dict.item_prices_html.wrapper).empty(); + + new wn.ui.form.TableGrid({ + parent: cur_frm.fields_dict.item_prices_html.wrapper, + frm: cur_frm, + table_field: wn.meta.get_docfield("Price List", "item_prices", cur_frm.doc.name) + }); +} + +wn.ui.form.TableGrid = Class.extend({ + init: function(opts) { + $.extend(this, opts); + this.fields = wn.meta.get_docfields("Item Price", cur_frm.doc.name); + this.make_table(); + }, + make_table: function() { + var me = this; + // Creating table & assigning attributes + var grid_table = document.createElement("table"); + grid_table.className = "table table-hover table-bordered table-grid"; + + // Appending header & rows to table + grid_table.appendChild(this.make_table_headers()); + grid_table.appendChild(this.make_table_rows()); + + // Creating button to add new row + var btn_div = document.createElement("div"); + var new_row_btn = document.createElement("button"); + new_row_btn.className = "btn btn-success table-new-row"; + new_row_btn.title = "Add new row"; + + var btn_icon = document.createElement("i"); + btn_icon.className = "icon-plus"; + new_row_btn.appendChild(btn_icon); + new_row_btn.innerHTML += " Add new row"; + btn_div.appendChild(new_row_btn); + + // Appending table & button to parent + var $grid_table = $(grid_table).appendTo($(this.parent)); + var $btn_div = $(btn_div).appendTo($(this.parent)); + + $btn_div.on("click", ".table-new-row", function() { + me.make_dialog(); + return false; + }); + + $grid_table.on("click", ".table-row", function() { + me.make_dialog(this); + return false; + }); + }, + make_table_headers: function() { + var me = this; + var header = document.createElement("thead"); + + // Creating header row + var row = document.createElement("tr"); + row.className = "active"; + + // Creating head first cell + var th = document.createElement("th"); + th.width = "8%"; + th.className = "text-center"; + th.innerHTML = "#"; + row.appendChild(th); + + // Make other headers with label as heading + for(var i=0, l=this.fields.length; i flt(max_qty): - curr_qty = (flt(max_qty) - flt(qty)) * flt(d.conversion_factor) + + already_received_qty = self.get_already_received_qty(d.prevdoc_docname, + d.prevdoc_detail_docname) + po_qty, ordered_warehouse = self.get_po_qty_and_warehouse(d.prevdoc_detail_docname) + + if not ordered_warehouse: + webnotes.throw(_("Warehouse is missing in Purchase Order")) + + if already_received_qty + d.qty > po_qty: + ordered_qty = - (po_qty - already_received_qty) * flt(d.conversion_factor) else: - curr_qty = flt(pr_qty) - - args = { + ordered_qty = - flt(d.qty) * flt(d.conversion_factor) + + update_bin({ "item_code": d.item_code, - "warehouse": d.warehouse, + "warehouse": ordered_warehouse, "posting_date": self.doc.posting_date, - "ordered_qty": (is_cancelled=="Yes" and -1 or 1)*flt(curr_qty) - } - update_bin(args) + "ordered_qty": flt(ordered_qty) if self.doc.docstatus==1 else -flt(ordered_qty) + }) + + def get_already_received_qty(self, po, po_detail): + qty = webnotes.conn.sql("""select sum(qty) from `tabPurchase Receipt Item` + where prevdoc_detail_docname = %s and docstatus = 1 + and prevdoc_doctype='Purchase Order' and prevdoc_docname=%s + and parent != %s""", (po_detail, po, self.doc.name)) + return qty and flt(qty[0][0]) or 0.0 + + def get_po_qty_and_warehouse(self, po_detail): + po_qty, po_warehouse = webnotes.conn.get_value("Purchase Order Item", po_detail, + ["qty", "warehouse"]) + return po_qty, po_warehouse def bk_flush_supp_wh(self, sl_entries): for d in getlist(self.doclist, 'pr_raw_material_details'): @@ -201,7 +209,7 @@ class DocType(BuyingController): sl_entries.append(self.get_sl_entries(d, { "item_code": d.rm_item_code, "warehouse": self.doc.supplier_warehouse, - "actual_qty": -1*flt(consumed_qty), + "actual_qty": -1*flt(d.consumed_qty), "incoming_rate": 0 })) @@ -281,7 +289,7 @@ class DocType(BuyingController): webnotes.conn.set(self.doc,'status','Cancelled') - self.update_ordered_qty(is_cancelled="Yes") + self.update_ordered_qty() self.update_stock() self.update_serial_nos(cancel=True) diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.py b/stock/doctype/stock_reconciliation/stock_reconciliation.py index 465edc490ad..9feb57e716b 100644 --- a/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -246,6 +246,7 @@ class DocType(StockController): "stock_uom": webnotes.conn.get_value("Item", row.item_code, "stock_uom"), "voucher_detail_no": row.voucher_detail_no, "fiscal_year": self.doc.fiscal_year, + "is_cancelled": "No" }) args.update(opts) self.make_sl_entries([args]) diff --git a/stock/doctype/warehouse/warehouse.py b/stock/doctype/warehouse/warehouse.py index faf9b04e00e..1bebc1405ad 100644 --- a/stock/doctype/warehouse/warehouse.py +++ b/stock/doctype/warehouse/warehouse.py @@ -209,5 +209,4 @@ class DocType: msgprint("""Warehosue can not be deleted as stock ledger entry exists for this warehouse.""", raise_exception=1) else: - webnotes.conn.sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name) - + webnotes.conn.sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name) \ No newline at end of file diff --git a/stock/page/stock_balance/stock_balance.js b/stock/page/stock_balance/stock_balance.js index 1bc5d1c6bc0..b45a610be88 100644 --- a/stock/page/stock_balance/stock_balance.js +++ b/stock/page/stock_balance/stock_balance.js @@ -126,10 +126,11 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({ } else { item.inflow_value += value_diff; } - } - item.closing_qty += qty_diff; - item.closing_value += value_diff; + item.closing_qty += qty_diff; + item.closing_value += value_diff; + } + } else { break; } diff --git a/stock/stock_ledger.py b/stock/stock_ledger.py index fe5b685f444..1555dab0bd8 100644 --- a/stock/stock_ledger.py +++ b/stock/stock_ledger.py @@ -14,29 +14,31 @@ _exceptions = webnotes.local('stockledger_exceptions') # _exceptions = [] def make_sl_entries(sl_entries, is_amended=None): - from stock.utils import update_bin + if sl_entries: + from stock.utils import update_bin - cancel = True if sl_entries[0].get("is_cancelled") == "Yes" else False - if cancel: - set_as_cancel(sl_entries[0].get('voucher_no'), sl_entries[0].get('voucher_type')) + cancel = True if sl_entries[0].get("is_cancelled") == "Yes" else False + if cancel: + set_as_cancel(sl_entries[0].get('voucher_no'), sl_entries[0].get('voucher_type')) - for sle in sl_entries: - sle_id = None - if sle.get('is_cancelled') == 'Yes': - sle['actual_qty'] = -flt(sle['actual_qty']) + for sle in sl_entries: + sle_id = None + if sle.get('is_cancelled') == 'Yes': + sle['actual_qty'] = -flt(sle['actual_qty']) - if sle.get("actual_qty"): - sle_id = make_entry(sle) + if sle.get("actual_qty"): + sle_id = make_entry(sle) - args = sle.copy() - args.update({ - "sle_id": sle_id, - "is_amended": is_amended - }) - update_bin(args) + args = sle.copy() + args.update({ + "sle_id": sle_id, + "is_amended": is_amended + }) + update_bin(args) - if cancel: - delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no')) + if cancel: + delete_cancelled_entry(sl_entries[0].get('voucher_type'), + sl_entries[0].get('voucher_no')) def set_as_cancel(voucher_type, voucher_no): webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes', diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 2535db7ab57..36231bf4f13 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -143,7 +143,8 @@ class TransactionBase(StatusUpdater): self.doc.fields.update(self.get_lead_defaults()) def get_customer_address(self, args): - args = load_json(args) + args = load_json(args) + webnotes.errprint(args) ret = { 'customer_address' : args["address"], 'address_display' : get_address_display(args["address"]), @@ -425,6 +426,7 @@ def get_address_territory(address_doc): def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company): """common validation for currency and price list currency""" + if conversion_rate == 0: msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True)