diff --git a/accounts/doctype/sales_invoice/pos.js b/accounts/doctype/sales_invoice/pos.js index 5045530aa95..1bd6de10303 100644 --- a/accounts/doctype/sales_invoice/pos.js +++ b/accounts/doctype/sales_invoice/pos.js @@ -5,10 +5,73 @@ erpnext.POS = Class.extend({ init: function(wrapper, frm) { this.wrapper = wrapper; this.frm = frm; - this.wrapper.html('
\ -
\ -
'); - + this.wrapper.html('
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
ItemQtyRate
\ +
\ +
\ +
\ + \ + \ + \ + \ + \ +
Net Total
\ + \ + \ + \ + \ + \ + \ +
Grand Total
\ +
\ +
\ +

\ + \ + \ +

\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
'); + this.make(); var me = this; @@ -16,10 +79,20 @@ erpnext.POS = Class.extend({ me.refresh(); }); + this.wrapper.find(".delete-items").on("click", function() { + me.remove_selected_item(); + }); + + this.wrapper.find(".make-payment").on("click", function() { + me.make_payment(); + }); }, make: function() { this.make_customer(); - this.make_items(); + this.make_item_group(); + this.make_search(); + this.make_barcode(); + this.make_item_list(); }, make_customer: function() { var me = this; @@ -28,7 +101,8 @@ erpnext.POS = Class.extend({ "fieldtype": "Link", "options": "Customer", "label": "Customer", - "fieldname": "pos_customer" + "fieldname": "pos_customer", + "placeholder": "Customer" }, parent: this.wrapper.find(".customer-area") }); @@ -36,25 +110,302 @@ erpnext.POS = Class.extend({ this.customer.$input.on("change", function() { if(!me.customer.autocomplete_open) wn.model.set_value("Sales Invoice", me.frm.docname, "customer", this.value); - }); - }, - make_items: function() { - var me = this; - this.wrapper.find(".btn-add").click(function() { - var child = wn.model.add_child(me.frm.doc, "Sales Invoice Item", "entries"); - child.item_code = "Test Item"; - me.frm.cscript.item_code(me.frm.doc, child.doctype, child.name); }); }, + make_item_group: function() { + var me = this; + this.item_group = wn.ui.form.make_control({ + df: { + "fieldtype": "Link", + "options": "Item Group", + "label": "Item Group", + "fieldname": "pos_item_group", + "placeholder": "Filter by Item Group" + }, + parent: this.wrapper.find(".item-group-area") + }); + this.item_group.make_input(); + this.item_group.$input.on("change", function() { + if(!me.item_group.autocomplete_open) + me.make_item_list(); + }); + }, + make_search: function() { + var me = this; + this.search = wn.ui.form.make_control({ + df: { + "fieldtype": "Link", + "options": "Item", + "label": "Item", + "fieldname": "pos_item", + "placeholder": "Select Item" + }, + parent: this.wrapper.find(".search-area") + }); + this.search.make_input(); + this.search.$input.on("change", function() { + if(!me.search.autocomplete_open) + me.make_item_list(); + }); + }, + make_barcode: function() { + var me = this; + this.barcode = wn.ui.form.make_control({ + df: { + "fieldtype": "Data", + "label": "Barcode", + "fieldname": "pos_barcode", + "placeholder": "Select Barcode" + }, + parent: this.wrapper.find(".barcode-area") + }); + this.barcode.make_input(); + this.barcode.$input.on("change", function() { + me.add_item_thru_barcode(); + }); + }, + make_item_list: function() { + var me = this; + wn.call({ + method: 'accounts.doctype.sales_invoice.pos.get_items', + args: { + price_list: cur_frm.doc.selling_price_list, + item_group: this.item_group.$input.val(), + item: this.search.$input.val() + }, + callback: function(r) { + var $wrap = me.wrapper.find(".item-list"); + me.wrapper.find(".item-list").empty(); + $.each(r.message, function(index, obj) { + if (obj.image) + image = ""; + else + image = '
'; + + $(repl('
\ + %(item_image)s\ +
%(item_code)s
\ +
%(item_name)s
\ +
%(item_price)s
\ +
', + { + item_code: obj.name, + item_price: format_currency(obj.ref_rate, obj.ref_currency), + item_name: obj.name===obj.item_name ? "" : obj.item_name, + item_image: image + })).appendTo($wrap); + }); + + $("div.pos-item").on("click", function() { + if(!cur_frm.doc.customer) { + msgprint("Please select customer first."); + return; + } + me.add_to_cart($(this).attr("data-item_code")); + }); + } + }); + }, + add_to_cart: function(item_code) { + var me = this; + var caught = false; + + // get no_of_items + no_of_items = me.wrapper.find("#cart tbody").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) { + if (d.item_code == item_code) + caught = true; + }); + } + + // if duplicate row then append the qty + if (caught) { + me.update_qty(item_code, 1); + } + else { + var child = wn.model.add_child(me.frm.doc, "Sales Invoice Item", "entries"); + child.item_code = item_code; + me.frm.cscript.item_code(me.frm.doc, child.doctype, child.name); + //me.refresh(); + } + }, + update_qty: function(item_code, qty) { + var me = this; + $.each(wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries", + "Sales Invoice"), function(i, d) { + if (d.item_code == item_code) { + if (qty == 1) + d.qty += 1; + else + d.qty = qty; + + me.frm.cscript.qty(me.frm.doc, d.doctype, d.name); + } + }); + me.refresh(); + }, refresh: function() { var me = this; this.customer.set_input(this.frm.doc.customer); - + this.barcode.set_input(""); + // add items - var $items = me.wrapper.find(".item-area").empty(); + 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) { - $(repl("
%(item_code)s
", d)).appendTo($items); + $(repl('\ + %(item_code)s%(item_name)s\ + \ + %(rate)s
%(amount)s\ + ', + { + item_code: d.item_code, + item_name: d.item_name===d.item_code ? "" : ("
" + 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) + } + )).appendTo($items); + }); + + // taxes + var taxes = wn.model.get_children("Sales Taxes and Charges", this.frm.doc.name, "other_charges", + "Sales Invoice"); + $(".tax-table") + .toggle((taxes && taxes.length) ? true : false) + .find("tbody").empty(); + + $.each(taxes, function(i, d) { + $(repl('\ + %(description)s\ + %(tax_amount)s\ + ', { + description: d.description, + tax_amount: format_currency(d.tax_amount, me.frm.doc.price_list_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)); + + // append quantity to the respective item after change from input box + $("input.qty").on("change", function() { + var item_code = $(this).closest("tr")[0].id; + me.update_qty(item_code, $(this).val()); + }); + + // on td click highlight the respective row + $("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(); + }, + refresh_delete_btn: function() { + $(".delete-items").toggle($(".item-cart .warning").length ? true : false); + }, + add_item_thru_barcode: function() { + var me = this; + wn.call({ + method: 'accounts.doctype.sales_invoice.pos.get_item_from_barcode', + args: {barcode: this.barcode.$input.val()}, + callback: function(r) { + if (r.message) { + me.add_to_cart(r.message[0].name); + me.refresh(); + } + else + msgprint(wn._("Invalid Barcode")); + } + }); + }, + remove_selected_item: function() { + var me = this; + var selected_items = []; + var no_of_items = $("#cart tbody tr").length; + for(var x=0; x<=no_of_items - 1; x++) { + var row = $("#cart tbody tr:eq(" + x + ")"); + if(row.attr("data-selected") == "true") { + selected_items.push(row.attr("id")); + } + } + + if (!selected_items[0]) + msgprint(wn._("Please select any item to remove it")); + + var child = wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries", + "Sales Invoice"); + $.each(child, function(i, d) { + for (var i in selected_items) { + if (d.item_code == selected_items[i]) { + wn.model.clear_doc(d.doctype, d.name); + } + } + }); + cur_frm.fields_dict["entries"].grid.refresh(); + me.refresh(); + }, + make_payment: function() { + var me = this; + var no_of_items = $("#cart tbody tr").length; + var mode_of_payment = []; + + if (no_of_items == 0) + msgprint(wn._("Payment cannot be made for empty cart")); + else { + wn.call({ + method: 'accounts.doctype.sales_invoice.pos.get_mode_of_payment', + callback: function(r) { + for (x=0; x<=r.message.length - 1; x++) { + mode_of_payment.push(r.message[x].name); + } + + // show payment wizard + var dialog = new wn.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:'Button', fieldname:'pay', label:'Pay'} + ] + }); + dialog.set_values({ + "total_amount": $(".grand-total").text() + }); + dialog.show(); + + dialog.get_input("total_amount").attr("disabled", "disabled"); + + 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.save(); + dialog.hide(); + me.refresh(); + }; + } }); - } -}) \ No newline at end of file + } + }, +}); \ No newline at end of file diff --git a/accounts/doctype/sales_invoice/pos.py b/accounts/doctype/sales_invoice/pos.py new file mode 100644 index 00000000000..2f2ad6a36c2 --- /dev/null +++ b/accounts/doctype/sales_invoice/pos.py @@ -0,0 +1,32 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import webnotes + +@webnotes.whitelist() +def get_items(price_list, item=None, item_group=None): + condition = "" + + if item_group and item_group != "All Item Groups": + condition = "and i.item_group='%s'" % item_group + + if item: + condition = "and i.name='%s'" % item + + return webnotes.conn.sql("""select + i.name, i.item_name, i.image, ip.ref_rate, ip.ref_currency + from `tabItem` i LEFT JOIN `tabItem Price` ip + ON ip.parent=i.name + and ip.price_list=%s + where + i.is_sales_item='Yes'%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_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 new file mode 100644 index 00000000000..e4b61b66ca8 --- /dev/null +++ b/accounts/doctype/sales_invoice/sales_invoice.css @@ -0,0 +1,15 @@ +.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 4001bea3c97..7e7f7d4126c 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -12,7 +12,7 @@ cur_frm.pformat.print_heading = 'Invoice'; 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'); +wn.require('app/accounts/doctype/sales_invoice/pos.js'); wn.provide("erpnext.accounts"); erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({ @@ -25,9 +25,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.frm.set_df_property("debit_to", "print_hide", 0); } } - // if(this.frm.doc.is_pos && this.frm.doc.docstatus===0) { - // cur_frm.cscript.toggle_pos(true); - // } + + cur_frm.cscript.toggle_pos(true); }, refresh: function(doc, dt, dn) { @@ -58,7 +57,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); } - if (this.frm.doc.docstatus===0) { + if (doc.docstatus===0) { cur_frm.add_custom_button(wn._('From Sales Order'), function() { wn.model.map_current_doc({ @@ -92,32 +91,62 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte } }); }); + + if(cint(sys_defaults.fs_pos_view)===1) + cur_frm.cscript.pos_btn(); - // cur_frm.add_custom_button(wn._("POS View"), function() { - // cur_frm.cscript.toggle_pos(); - // }, 'icon-desktop'); - + // setTimeout(function() { cur_frm.$pos_btn.click(); }, 1000); + + } else { + // hide shown pos for submitted records + if(cur_frm.pos_active) cur_frm.cscript.toggle_pos(false); } }, + 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"; + } else { + var btn_label = wn._("Invoice View"), + icon = "icon-file-text"; + } + + 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((show===true && cur_frm.pos_active) || (show===false && !cur_frm.pos_active)) return; + if(cint(sys_defaults.fs_pos_view)===0) return; + if(!(this.frm.doc.is_pos && this.frm.doc.docstatus===0)) 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); + 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(); } - - // 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(); }, diff --git a/config.json b/config.json index c9d1aa71f81..5412b017b24 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,5 @@ { + "app_name": "ERPNext", "modules": { "Selling": { "link": "selling-home", diff --git a/patches/may_2013/p06_make_notes.py b/patches/may_2013/p06_make_notes.py index b60642c53ec..29bfe25da40 100644 --- a/patches/may_2013/p06_make_notes.py +++ b/patches/may_2013/p06_make_notes.py @@ -13,17 +13,27 @@ def execute(): name = question.question[:180] if webnotes.conn.exists("Note", name): webnotes.delete_doc("Note", name) - note = webnotes.bean({ + + similar_questions = webnotes.conn.sql_list("""select name from `tabQuestion` + where question like %s""", "%s%%" % name) + answers = [markdown2.markdown(c) for c in webnotes.conn.sql_list(""" + select answer from tabAnswer where question in (%s)""" % \ + ", ".join(["%s"]*len(similar_questions)), similar_questions)] + + webnotes.bean({ "doctype":"Note", "title": name, - "content": "
".join([markdown2.markdown(c) for c in webnotes.conn.sql_list(""" - select answer from tabAnswer where question=%s""", question.name)]), + "content": "
".join(answers), "owner": question.owner, "creation": question.creation, "public": 1 }).insert() + except NameError: pass + except Exception, e: + if e.args[0] != 1062: + raise e webnotes.delete_doc("DocType", "Question") webnotes.delete_doc("DocType", "Answer") diff --git a/public/js/transaction.js b/public/js/transaction.js index 118594c8160..1d06a9f487d 100644 --- a/public/js/transaction.js +++ b/public/js/transaction.js @@ -6,9 +6,9 @@ wn.require("app/js/controllers/stock_controller.js"); erpnext.TransactionController = erpnext.stock.StockController.extend({ onload: function() { + var me = this; if(this.frm.doc.__islocal) { - var me = this, - today = get_today(), + var today = get_today(), currency = wn.defaults.get_default("currency"); $.each({ @@ -30,6 +30,14 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ me.frm.script_manager.trigger("company"); } + + if(this.other_fname) { + this[this.other_fname + "_remove"] = this.calculate_taxes_and_totals; + } + + if(this.fname) { + this[this.fname + "_remove"] = this.calculate_taxes_and_totals; + } }, onload_post_render: function() { @@ -311,9 +319,14 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ function(item_code, tax_data) { if(!item_tax[item_code]) item_tax[item_code] = {}; if($.isArray(tax_data)) { - var tax_rate = tax_data[0] == null ? "" : (flt(tax_data[0], tax_rate_precision) + "%"), - tax_amount = format_currency(flt(tax_data[1], tax_amount_precision), company_currency, - tax_amount_precision); + var tax_rate = ""; + if(tax_data[0] != null) { + tax_rate = (tax.charge_type === "Actual") ? + format_currency(flt(tax_data[0], tax_amount_precision), company_currency, tax_amount_precision) : + (flt(tax_data[0], tax_rate_precision) + "%"); + } + var tax_amount = format_currency(flt(tax_data[1], tax_amount_precision), company_currency, + tax_amount_precision); item_tax[item_code][tax.name] = [tax_rate, tax_amount]; } else { diff --git a/selling/doctype/sales_bom/sales_bom.py b/selling/doctype/sales_bom/sales_bom.py index 8bcac1771f2..15d8fd1e5b7 100644 --- a/selling/doctype/sales_bom/sales_bom.py +++ b/selling/doctype/sales_bom/sales_bom.py @@ -76,8 +76,8 @@ class DocType: def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): from controllers.queries import get_match_cond - - return webnotes.conn.sql("""select name, description from tabItem + + return webnotes.conn.sql("""select name, item_name, description from tabItem where is_stock_item="No" and is_sales_item="Yes" and name not in (select name from `tabSales BOM`) and %s like %s %s limit %s, %s""" % (searchfield, "%s", diff --git a/selling/doctype/sales_order_item/sales_order_item.txt b/selling/doctype/sales_order_item/sales_order_item.txt index 9cf2a0360f2..47b4d6b870b 100644 --- a/selling/doctype/sales_order_item/sales_order_item.txt +++ b/selling/doctype/sales_order_item/sales_order_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 11:42:58", "docstatus": 0, - "modified": "2013-08-22 15:21:56", + "modified": "2013-08-22 15:43:07", "modified_by": "Administrator", "owner": "Administrator" }, diff --git a/setup/doctype/features_setup/features_setup.txt b/setup/doctype/features_setup/features_setup.txt index e1a4c08acfa..89c9dd310b9 100644 --- a/setup/doctype/features_setup/features_setup.txt +++ b/setup/doctype/features_setup/features_setup.txt @@ -2,7 +2,7 @@ { "creation": "2012-12-20 12:50:49", "docstatus": 0, - "modified": "2013-07-05 14:37:59", + "modified": "2013-08-22 15:36:43", "modified_by": "Administrator", "owner": "Administrator" }, @@ -184,6 +184,13 @@ "fieldtype": "Check", "label": "Point of Sale" }, + { + "description": "To enable Point of Sale view", + "doctype": "DocField", + "fieldname": "fs_pos_view", + "fieldtype": "Check", + "label": "POS View" + }, { "doctype": "DocField", "fieldname": "production", diff --git a/startup/install.py b/startup/install.py index 7e9273ca586..ea281b8ea77 100644 --- a/startup/install.py +++ b/startup/install.py @@ -72,7 +72,7 @@ def feature_setup(): 'fs_exports', 'fs_imports', 'fs_discounts', 'fs_purchase_discounts', 'fs_after_sales_installations', 'fs_projects', 'fs_sales_extras', 'fs_recurring_invoice', 'fs_pos', 'fs_manufacturing', 'fs_quality', - 'fs_page_break', 'fs_more_info' + 'fs_page_break', 'fs_more_info', 'fs_pos_view' ] doc.fields.update(dict(zip(flds, [1]*len(flds)))) doc.save() diff --git a/stock/doctype/purchase_receipt/purchase_receipt_list.js b/stock/doctype/purchase_receipt/purchase_receipt_list.js deleted file mode 100644 index bc0c9f6e00a..00000000000 --- a/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. -// License: GNU General Public License v3. See license.txt - -// render -wn.listview_settings['Purchase Receipt'] = { - add_fields: ["group_concat(`tabPurchase Receipt Item`.prevdoc_docname) \ - as purchase_order_no"], - add_columns: [{"content":"purchase_order_no", width:"30%"}], - group_by: "`tabPurchase Receipt`.name", - prepare_data: function(data) { - if(data.purchase_order_no) { - data.purchase_order_no = $.unique(data.purchase_order_no.split(",")); - var po_list = []; - $.each(data.purchase_order_no, function(i, v){ - if(po_list.indexOf(v)==-1) po_list.push( - repl("%(name)s", - {name: v})); - }); - data.purchase_order_no = po_list.join(", "); - } - } -}; diff --git a/stock/utils.py b/stock/utils.py index b071d753226..f0e948177b6 100644 --- a/stock/utils.py +++ b/stock/utils.py @@ -248,8 +248,7 @@ def reorder_item(): and exists (select name from `tabItem` where `tabItem`.name = `tabBin`.item_code and is_stock_item='Yes' and (is_purchase_item='Yes' or is_sub_contracted_item='Yes') and - (ifnull(end_of_life, '')='') or end_of_life > now())""", - as_dict=True) + (ifnull(end_of_life, '')='' or end_of_life > now()))""", as_dict=True) for bin in bin_list: #check if re-order is required item_reorder = webnotes.conn.get("Item Reorder", @@ -389,4 +388,4 @@ def repost(): """ from webnotes.model.code import get_obj for wh in webnotes.conn.sql("select name from tabWarehouse"): - get_obj('Warehouse', wh[0]).repost_stock() \ No newline at end of file + get_obj('Warehouse', wh[0]).repost_stock() diff --git a/utilities/demo/__init__.py b/utilities/demo/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/utilities/demo/demo-login.css b/utilities/demo/demo-login.css new file mode 100644 index 00000000000..f3464a3a2d7 --- /dev/null +++ b/utilities/demo/demo-login.css @@ -0,0 +1,3 @@ +body, #container, .outer { + background-color: #888 !important; +} \ No newline at end of file diff --git a/utilities/demo/demo-login.html b/utilities/demo/demo-login.html new file mode 100644 index 00000000000..ef24678e27a --- /dev/null +++ b/utilities/demo/demo-login.html @@ -0,0 +1,25 @@ +
+
+
+
+
+ Start ERPNext Demo +
+
+

+ +

+

+ +

+
+

Some functionality is disabled for the demo app. The demo data will be cleared regulary. To start your own ERPNext Trial, click here

+
+
+
+
+
+
+
diff --git a/utilities/demo/demo-login.js b/utilities/demo/demo-login.js new file mode 100644 index 00000000000..229d1690e52 --- /dev/null +++ b/utilities/demo/demo-login.js @@ -0,0 +1,27 @@ +$(document).ready(function() { + $(".navbar, footer, .banner, #user-tools").toggle(false); + + $("#login_btn").click(function() { + var me = this; + $(this).html("Logging In...").attr("disabled", "disabled"); + wn.call({ + "method": "login", + args: { + usr: "demo@erpnext.com", + pwd: "demo", + lead_email: $("#lead-email").val(), + }, + callback: function(r) { + $(me).attr("disabled", false); + if(r.exc) { + alert("Error, please contact support@erpnext.com"); + } else { + console.log("Logged In"); + window.location.href = "app.html"; + } + } + }) + return false; + }) + .attr("disabled", false); +}) \ No newline at end of file diff --git a/utilities/demo/demo_control_panel.py b/utilities/demo/demo_control_panel.py new file mode 100644 index 00000000000..c70913ee68a --- /dev/null +++ b/utilities/demo/demo_control_panel.py @@ -0,0 +1,13 @@ + + def on_login(self): + from webnotes.utils import validate_email_add + import conf + if hasattr(conf, "demo_notify_url"): + if webnotes.form_dict.lead_email and validate_email_add(webnotes.form_dict.lead_email): + import requests + response = requests.post(conf.demo_notify_url, data={ + "cmd":"website.helpers.contact.send_message", + "subject":"Logged into Demo", + "sender": webnotes.form_dict.lead_email, + "message": "via demo.erpnext.com" + }) diff --git a/utilities/demo_docs/Address.csv b/utilities/demo/demo_docs/Address.csv similarity index 100% rename from utilities/demo_docs/Address.csv rename to utilities/demo/demo_docs/Address.csv diff --git a/utilities/demo_docs/BOM.csv b/utilities/demo/demo_docs/BOM.csv similarity index 100% rename from utilities/demo_docs/BOM.csv rename to utilities/demo/demo_docs/BOM.csv diff --git a/utilities/demo_docs/Contact.csv b/utilities/demo/demo_docs/Contact.csv similarity index 100% rename from utilities/demo_docs/Contact.csv rename to utilities/demo/demo_docs/Contact.csv diff --git a/utilities/demo_docs/Customer.csv b/utilities/demo/demo_docs/Customer.csv similarity index 100% rename from utilities/demo_docs/Customer.csv rename to utilities/demo/demo_docs/Customer.csv diff --git a/utilities/demo_docs/Employee.csv b/utilities/demo/demo_docs/Employee.csv similarity index 100% rename from utilities/demo_docs/Employee.csv rename to utilities/demo/demo_docs/Employee.csv diff --git a/utilities/demo_docs/Fiscal_Year.csv b/utilities/demo/demo_docs/Fiscal_Year.csv similarity index 100% rename from utilities/demo_docs/Fiscal_Year.csv rename to utilities/demo/demo_docs/Fiscal_Year.csv diff --git a/utilities/demo_docs/Item.csv b/utilities/demo/demo_docs/Item.csv similarity index 100% rename from utilities/demo_docs/Item.csv rename to utilities/demo/demo_docs/Item.csv diff --git a/utilities/demo_docs/Item_Price.csv b/utilities/demo/demo_docs/Item_Price.csv similarity index 100% rename from utilities/demo_docs/Item_Price.csv rename to utilities/demo/demo_docs/Item_Price.csv diff --git a/utilities/demo_docs/Lead.csv b/utilities/demo/demo_docs/Lead.csv similarity index 100% rename from utilities/demo_docs/Lead.csv rename to utilities/demo/demo_docs/Lead.csv diff --git a/utilities/demo_docs/Profile.csv b/utilities/demo/demo_docs/Profile.csv similarity index 100% rename from utilities/demo_docs/Profile.csv rename to utilities/demo/demo_docs/Profile.csv diff --git a/utilities/demo_docs/Salary_Structure.csv b/utilities/demo/demo_docs/Salary_Structure.csv similarity index 100% rename from utilities/demo_docs/Salary_Structure.csv rename to utilities/demo/demo_docs/Salary_Structure.csv diff --git a/utilities/demo_docs/Stock Reconcilation Template.csv b/utilities/demo/demo_docs/Stock Reconcilation Template.csv similarity index 100% rename from utilities/demo_docs/Stock Reconcilation Template.csv rename to utilities/demo/demo_docs/Stock Reconcilation Template.csv diff --git a/utilities/demo_docs/Supplier.csv b/utilities/demo/demo_docs/Supplier.csv similarity index 100% rename from utilities/demo_docs/Supplier.csv rename to utilities/demo/demo_docs/Supplier.csv diff --git a/utilities/demo_docs/bearing-block.png b/utilities/demo/demo_docs/bearing-block.png similarity index 100% rename from utilities/demo_docs/bearing-block.png rename to utilities/demo/demo_docs/bearing-block.png diff --git a/utilities/demo_docs/wind-turbine.png b/utilities/demo/demo_docs/wind-turbine.png similarity index 100% rename from utilities/demo_docs/wind-turbine.png rename to utilities/demo/demo_docs/wind-turbine.png diff --git a/utilities/make_demo.py b/utilities/demo/make_demo.py similarity index 93% rename from utilities/make_demo.py rename to utilities/demo/make_demo.py index 809a12b6c5c..fc0776740e6 100644 --- a/utilities/make_demo.py +++ b/utilities/demo/make_demo.py @@ -19,18 +19,19 @@ company_abbr = "WP" country = "United States" currency = "USD" time_zone = "America/New York" -start_date = '2010-01-01' +start_date = '2013-01-01' bank_name = "Citibank" -runs_for = 20 +runs_for = None prob = { "default": { "make": 0.6, "qty": (1,5) }, + "Sales Order": { "make": 0.4, "qty": (1,3) }, "Purchase Order": { "make": 0.7, "qty": (1,15) }, "Purchase Receipt": { "make": 0.7, "qty": (1,15) }, } def make(reset=False): webnotes.connect() - webnotes.print_messages = True + #webnotes.print_messages = True webnotes.mute_emails = True webnotes.rollback_on_exception = True @@ -49,21 +50,24 @@ def setup(): # make_opening_accounts() def simulate(): - current_date = None - for i in xrange(runs_for): - if not current_date: - # get last stock ledger posting date or use default - last_posting = webnotes.conn.sql("""select max(posting_date) from `tabStock Ledger Entry`""") - if last_posting[0][0]: - current_date = webnotes.utils.add_days(last_posting[0][0], 1) - else: - current_date = webnotes.utils.getdate(start_date) - else: - current_date = webnotes.utils.add_days(current_date, 1) - + global runs_for + current_date = webnotes.utils.getdate(start_date) + + # continue? + last_posting = webnotes.conn.sql("""select max(posting_date) from `tabStock Ledger Entry`""") + if last_posting[0][0]: + current_date = webnotes.utils.add_days(last_posting[0][0], 1) + + # run till today + if not runs_for: + runs_for = webnotes.utils.date_diff(webnotes.utils.nowdate(), current_date) + + for i in xrange(runs_for): print current_date.strftime("%Y-%m-%d") + webnotes.utils.current_date = current_date if current_date.weekday() in (5, 6): + current_date = webnotes.utils.add_days(current_date, 1) continue run_sales(current_date) @@ -71,6 +75,8 @@ def simulate(): run_manufacturing(current_date) run_stock(current_date) run_accounts(current_date) + + current_date = webnotes.utils.add_days(current_date, 1) def run_sales(current_date): if can_make("Quotation"): @@ -135,7 +141,7 @@ def run_stock(current_date): for po in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Purchase Receipt")]: pr = webnotes.bean(make_purchase_receipt(po)) pr.doc.posting_date = current_date - pr.doc.fiscal_year = "2010" + pr.doc.fiscal_year = "2013" pr.insert() pr.submit() webnotes.conn.commit() @@ -149,7 +155,7 @@ def run_stock(current_date): for so in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Delivery Note")]: dn = webnotes.bean(make_delivery_note(so)) dn.doc.posting_date = current_date - dn.doc.fiscal_year = "2010" + dn.doc.fiscal_year = "2013" dn.insert() try: dn.submit() @@ -172,7 +178,7 @@ def run_purchase(current_date): mr = webnotes.new_bean("Material Request") mr.doc.material_request_type = "Purchase" mr.doc.transaction_date = current_date - mr.doc.fiscal_year = "2010" + mr.doc.fiscal_year = "2013" mr.doclist.append({ "doctype": "Material Request Item", "parentfield": "indent_details", @@ -191,7 +197,7 @@ def run_purchase(current_date): if row[0] != "Total": sq = webnotes.bean(make_supplier_quotation(row[0])) sq.doc.transaction_date = current_date - sq.doc.fiscal_year = "2010" + sq.doc.fiscal_year = "2013" sq.insert() sq.submit() webnotes.conn.commit() @@ -204,7 +210,7 @@ def run_purchase(current_date): if row[0] != "Total": po = webnotes.bean(make_purchase_order(row[0])) po.doc.transaction_date = current_date - po.doc.fiscal_year = "2010" + po.doc.fiscal_year = "2013" po.insert() po.submit() webnotes.conn.commit() @@ -262,7 +268,7 @@ def make_stock_entry_from_pro(pro_id, purpose, current_date): st = webnotes.bean(make_stock_entry(pro_id, purpose)) st.doc.posting_date = current_date - st.doc.fiscal_year = "2010" + st.doc.fiscal_year = "2013" st.doc.expense_adjustment_account = "Stock in Hand - WP" try: st.insert() @@ -281,7 +287,7 @@ def make_quotation(current_date): "customer": get_random("Customer"), "order_type": "Sales", "transaction_date": current_date, - "fiscal_year": "2010" + "fiscal_year": "2013" }]) add_random_children(b, { @@ -348,8 +354,9 @@ def how_many(doctype): def install(): print "Creating Fresh Database..." from webnotes.install_lib.install import Installer + import conf inst = Installer('root') - inst.import_from_db("demo", verbose = 1) + inst.import_from_db(conf.demo_db_name, verbose = 1) def complete_setup(): print "Complete Setup..." @@ -391,6 +398,7 @@ def make_bank_account(): }).insert() webnotes.set_value("Company", company, "default_bank_account", ba.doc.name) + webnotes.conn.commit() def import_data(dt, submit=False): if not isinstance(dt, (tuple, list)): diff --git a/utilities/demo/make_erpnext_demo.py b/utilities/demo/make_erpnext_demo.py new file mode 100644 index 00000000000..0a8a9351d8f --- /dev/null +++ b/utilities/demo/make_erpnext_demo.py @@ -0,0 +1,81 @@ +if __name__=="__main__": + import sys + sys.path.extend([".", "lib", "app"]) + +import webnotes, os + +def make_demo_app(): + import utilities.demo.make_demo + utilities.demo.make_demo.make(reset=True) + +def make_demo_user(): + if webnotes.conn.exists("Profile", "demo@erpnext.com"): + webnotes.delete_doc("Profile", "demo@erpnext.com") + + p = webnotes.new_bean("Profile") + p.doc.email = "demo@erpnext.com" + p.doc.first_name = "Demo" + p.doc.last_name = "User" + p.doc.enabled = 1 + p.doc.user_type = "ERPNext Demo" + p.doc.send_invite_email = 0 + p.doc.new_password = "demo" + p.insert() + + for role in ("Accounts Manager", "Analytics", "Expense Approver", "Accounts User", + "Leave Approver", "Blogger", "Customer", "Sales Manager", "Employee", "Support Manager", + "HR Manager", "HR User", "Maintenance Manager", "Maintenance User", "Material Manager", + "Material Master Manager", "Material User", "Partner", "Manufacturing Manager", + "Manufacturing User", "Projects User", "Purchase Manager", "Purchase Master Manager", + "Purchase User", "Quality Manager", "Report Manager", "Sales Master Manager", "Sales User", + "Supplier", "Support Team"): + p.doclist.append({ + "doctype": "UserRole", + "parentfield": "user_roles", + "role": role + }) + + p.save() + webnotes.conn.commit() + +def make_demo_login_page(): + webnotes.conn.set_value("Website Settings", None, "home_page", "") + + webnotes.conn.sql("""delete from `tabWeb Page` where name='demo-login'""") + p = webnotes.new_bean("Web Page") + p.doc.title = "Demo Login" + p.doc.published = 1 + p.doc.description = "ERPNext Demo Login" + + with open(os.path.join(os.path.dirname(__file__), "demo-login.html"), "r") as dfile: + p.doc.main_section = dfile.read() + + p.doc.insert_code = 1 + with open(os.path.join(os.path.dirname(__file__), "demo-login.js"), "r") as dfile: + p.doc.javascript = dfile.read() + + p.doc.insert_style = 1 + with open(os.path.join(os.path.dirname(__file__), "demo-login.css"), "r") as dfile: + p.doc.css = dfile.read() + + p.insert() + + webnotes.conn.set_value("Website Settings", None, "home_page", "demo-login") + + webnotes.conn.commit() + +def make_demo_on_login_script(): + webnotes.conn.sql("""delete from `tabCustom Script` where dt='Control Panel'""") + s = webnotes.new_bean("Custom Script") + s.doc.dt = "Control Panel" + s.doc.script_type = "Server" + with open(os.path.join(os.path.dirname(__file__), "demo_control_panel.py"), "r") as dfile: + s.doc.script = dfile.read() + +if __name__=="__main__": + webnotes.connect() + webnotes.mute_emails = 1 + make_demo_app() + make_demo_user() + make_demo_login_page() + make_demo_on_login_script() \ No newline at end of file diff --git a/website/doctype/website_settings/website_settings.js b/website/doctype/website_settings/website_settings.js index 0d0dab68c08..21b55be5ea9 100644 --- a/website/doctype/website_settings/website_settings.js +++ b/website/doctype/website_settings/website_settings.js @@ -4,6 +4,14 @@ // update parent select $.extend(cur_frm.cscript, { + refresh: function(doc) { + cur_frm.add_custom_button("Auto Build Website", function() { + cur_frm.call({ + doc: cur_frm.doc, + method: "make_website" + }) + }, 'icon-magic') + }, onload_post_render: function(doc) { this.set_parent_label_options(); }, diff --git a/website/doctype/website_settings/website_settings.py b/website/doctype/website_settings/website_settings.py index 3b27c7ccc1d..5d6c874d702 100644 --- a/website/doctype/website_settings/website_settings.py +++ b/website/doctype/website_settings/website_settings.py @@ -13,6 +13,26 @@ class DocType: self.set_home_page() self.validate_top_bar_items() self.validate_footer_items() + + def make_website(self): + # set item pages + for name in webnotes.conn.sql_list("""select name from tabItem where + ifnull(show_in_website, 0)=0 and is_sales_item ='Yes' """): + webnotes.msgprint("Setting 'Show in Website' for:" + name) + item = webnotes.bean("Item", name) + item.doc.show_in_website = 1 + item.doc.website_warehouse = item.doc.default_warehouse + item.doc.website_image = item.doc.image + item.save() + + # set item group pages + for name in webnotes.conn.sql_list("""select name from `tabItem Group` where + ifnull(show_in_website, 0)=0 and exists (select name from tabItem where + ifnull(show_in_website, 0)=1)"""): + webnotes.msgprint("Setting 'Show in Website' for:" + name) + item_group = webnotes.bean("Item Group", name) + item_group.doc.show_in_website = 1 + item_group.save() def validate_top_bar_items(self): """validate url in top bar items"""